Full Code of kean/VPN for AI

main 9ec64909ea44 cached
34 files
95.1 KB
27.0k tokens
1 requests
Download .txt
Repository: kean/VPN
Branch: main
Commit: 9ec64909ea44
Files: 34
Total size: 95.1 KB

Directory structure:
gitextract_dp8p67_4/

├── .gitignore
├── README.md
├── vpn-client/
│   ├── AppDelegate.swift
│   ├── Assets.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   └── icon-vpn.imageset/
│   │       └── Contents.json
│   ├── Base.lproj/
│   │   └── LaunchScreen.storyboard
│   ├── Info.plist
│   ├── Preview Content/
│   │   └── Preview Assets.xcassets/
│   │       └── Contents.json
│   ├── SceneDelegate.swift
│   ├── Screens/
│   │   ├── PrimaryButton.swift
│   │   ├── PrimaryButtonView.swift
│   │   ├── RouterView.swift
│   │   ├── Spinner.swift
│   │   ├── SplashView.swift
│   │   ├── TunnelDetails/
│   │   │   ├── TunnelDetailsView.swift
│   │   │   └── TunnelDetailsViewModel.swift
│   │   └── WelcomeView.swift
│   ├── Services/
│   │   ├── Keychain.swift
│   │   └── VPNConfigurationService.swift
│   └── vpn-client.entitlements
├── vpn-protocol/
│   ├── Cipher.swift
│   ├── Packet.swift
│   └── Session.swift
├── vpn-server/
│   └── main.swift
├── vpn-tunnel/
│   ├── Info.plist
│   ├── PacketTunnelProvider.swift
│   └── vpn_tunnel.entitlements
└── vpn.xcodeproj/
    ├── project.pbxproj
    ├── project.xcworkspace/
    │   ├── contents.xcworkspacedata
    │   └── xcshareddata/
    │       ├── IDEWorkspaceChecks.plist
    │       └── swiftpm/
    │           └── Package.resolved
    └── xcshareddata/
        └── xcschemes/
            ├── vpn-client.xcscheme
            └── vpn-tunnel.xcscheme

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
## System
.DS_Store

## Build generated
build/
DerivedData
Nuke.xcodeproj/xcshareddata/xcbaselines/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata

## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
*.ipa

## Playgrounds
timeline.xctimeline
playground.xcworkspace


## Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/

.build/


## CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#

Pods/


## Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.

Carthage


================================================
FILE: README.md
================================================
# VPN (WIP)

A sample VPN client/server written in Swift.

# License

VPN is available under the MIT license. See the LICENSE file for more info.


================================================
FILE: vpn-client/AppDelegate.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}



================================================
FILE: vpn-client/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "20x20"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "20x20"
    },
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "29x29"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "29x29"
    },
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "40x40"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "40x40"
    },
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "60x60"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "60x60"
    },
    {
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "20x20"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "20x20"
    },
    {
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "29x29"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "29x29"
    },
    {
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "40x40"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "40x40"
    },
    {
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "76x76"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "76x76"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "83.5x83.5"
    },
    {
      "idiom" : "ios-marketing",
      "scale" : "1x",
      "size" : "1024x1024"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: vpn-client/Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: vpn-client/Assets.xcassets/icon-vpn.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "icons8-vpn-status-bar-icon-50.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "icons8-vpn-status-bar-icon-100.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "icons8-vpn-status-bar-icon-101.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "properties" : {
    "template-rendering-intent" : "template"
  }
}


================================================
FILE: vpn-client/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
</document>


================================================
FILE: vpn-client/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UIApplicationSceneManifest</key>
	<dict>
		<key>UIApplicationSupportsMultipleScenes</key>
		<false/>
		<key>UISceneConfigurations</key>
		<dict>
			<key>UIWindowSceneSessionRoleApplication</key>
			<array>
				<dict>
					<key>UISceneConfigurationName</key>
					<string>Default Configuration</string>
					<key>UISceneDelegateClassName</key>
					<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
				</dict>
			</array>
		</dict>
	</dict>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
</dict>
</plist>


================================================
FILE: vpn-client/Preview Content/Preview Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: vpn-client/SceneDelegate.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Create the SwiftUI view that provides the window contents.
        let contentView = RouterView()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }


}



================================================
FILE: vpn-client/Screens/PrimaryButton.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import SwiftUI

struct PrimaryButton: View {
    let title: String
    let action: () -> Void
    @Binding var isLoading: Bool

    var body: some View {
        Button(action: self.action) {
            ZStack {
                Spinner(isAnimating: $isLoading, color: .white, style: .medium)
                Text(title)
                    .opacity(isLoading ? 0 : 1)
            }
        }
            .disabled(isLoading)
            .padding()
            .frame(maxWidth: .infinity)
            .foregroundColor(Color.white)
            .background(Color.blue)
            .cornerRadius(8)
    }
}

struct PrimaryButtonView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            PrimaryButton(title: "Action", action: {}, isLoading: .constant(false))
                .previewLayout(.fixed(width: 300, height: 80))

            PrimaryButton(title: "Action", action: {}, isLoading: .constant(false))
                .previewLayout(.fixed(width: 300, height: 80))
                .environment(\.colorScheme, .dark)

            PrimaryButton(title: "Action", action: {}, isLoading: .constant(true))
                .previewLayout(.fixed(width: 300, height: 80))
        }
    }
}


================================================
FILE: vpn-client/Screens/PrimaryButtonView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import SwiftUI

struct PrimaryButton: View {
    let title: String
    let action: () -> Void
    @Binding var isLoading: Bool

    var body: some View {
        Button(action: self.action) {
            ZStack {
                Spinner(isAnimating: .constant(true), style: .medium)
                Text(title)
            }
        }
            .padding()
            .foregroundColor(Color.white)
            .background(Color.blue)
            .cornerRadius(8)
    }
}

struct PrimaryButtonView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            PrimaryButton(title: "Action", action: {}, isLoading: .constant(false))
                .previewLayout(.fixed(width: 300, height: 80))

            PrimaryButton(title: "Action", action: {}, isLoading: .constant(false))
                .previewLayout(.fixed(width: 300, height: 80))
                .environment(\.colorScheme, .dark)

            PrimaryButton(title: "Action", action: {}, isLoading: .constant(true))
                .previewLayout(.fixed(width: 300, height: 80))
        }
    }
}


================================================
FILE: vpn-client/Screens/RouterView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import SwiftUI

struct RouterView: View {
    @ObservedObject var service: VPNConfigurationService = .shared

    var body: some View {
        if !service.isStarted {
            return AnyView(SplashView())
        } else {
            if let tunnel = service.tunnel {
                return AnyView(TunnelDetailsView(model: TunnelViewModel(tunnel: tunnel)))
            } else {
                return AnyView(WelcomeView())
            }
        }
    }
}

struct RouterView_Previews: PreviewProvider {
    static var previews: some View {
        RouterView()
    }
}


================================================
FILE: vpn-client/Screens/Spinner.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import SwiftUI
import UIKit

struct Spinner: UIViewRepresentable {
    @Binding var isAnimating: Bool

    let color: UIColor
    let style: UIActivityIndicatorView.Style

    func makeUIView(context: Context) -> UIActivityIndicatorView {
        let indicator = UIActivityIndicatorView(style: style)
        indicator.hidesWhenStopped = true
        indicator.color = color
        return indicator
    }

    func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {
        isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
    }
}


================================================
FILE: vpn-client/Screens/SplashView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import SwiftUI
import NetworkExtension

struct SplashView: View {
    @ObservedObject var service: VPNConfigurationService = .shared

    @State private var isLoading = false
    @State private var isShowingError = false
    @State private var errorMessage = ""

    var body: some View {
        VStack {
            Text("BestVPN")
                .font(.largeTitle)
                .fontWeight(.heavy)
            Spacer().frame(height: 32)
            HStack {
                Text("Loading profiles…")
                Spinner(isAnimating: $isLoading, color: .label, style: .medium)
            }
        }
        .onAppear(perform: refresh)
        .alert(isPresented: $isShowingError) {
            Alert(
                title: Text("Failed to load profiles"),
                message: Text(errorMessage),
                primaryButton: .default(Text("Retry"), action: refresh),
                secondaryButton: .cancel()
            )
        }
    }

    private func refresh() {
        isLoading = true
        service.refresh {
            self.isLoading = false
            switch $0 {
            case .success:
                break // Handled by RouterView
            case let .failure(error):
                self.errorMessage = error.localizedDescription
                self.isShowingError = true
            }
        }
    }
}

struct SplashView_Previews: PreviewProvider {
    static var previews: some View {
        SplashView()
    }
}


================================================
FILE: vpn-client/Screens/TunnelDetails/TunnelDetailsView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import SwiftUI

struct TunnelDetailsView: View {
    @ObservedObject var model: TunnelViewModel

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Settings")) {
                    TextInputView(title: "Username", text: $model.username)
                    TextInputView(title: "Password", text: $model.password)
                    TextInputView(title: "Server", text: $model.server)
                    Button(action: model.buttonSaveTapped) { Text("Save") }
                        .foregroundColor(Color.blue)
                }
                Section(header: Text("Status")) {
                    Toggle(isOn: $model.isEnabled, label: { Text("Enabled") })
                    if model.isEnabled {
                        Text("Status: ") + Text(model.status).bold()
                        if model.isStarted {
                            Button(action: model.buttonStopTapped) { Text("Stop") }
                                .foregroundColor(Color.orange)
                        } else {
                            Button(action: model.buttonStartTapped) { Text("Start") }
                                .foregroundColor(Color.blue)
                        }
                    }
                }
                Section {
                    ButtonRemoveProfile(model: model)
                }
            }
            .disabled(model.isLoading)
            .alert(isPresented: $model.isShowingError) {
                Alert(
                    title: Text(self.model.errorTitle),
                    message: Text(self.model.errorMessage),
                    dismissButton: .cancel()
                )
            }
            .navigationBarItems(trailing:
                Spinner(isAnimating: $model.isLoading, color: .label, style: .medium)
            )
            .navigationBarTitle("VPN Status")
        }
    }
}

private struct TextInputView: View {
    let title: String
    let text: Binding<String>

    var body: some View {
        HStack(alignment: .center) {
            Text(title)
                .font(.callout)
            TextField(title, text: text)
                .multilineTextAlignment(.trailing)
                .foregroundColor(Color.gray)
        }
    }
}

private struct ButtonRemoveProfile: View {
    let model: TunnelViewModel

    @State private var isConfirmationPresented = false

    var body: some View {
        Button(action: {
            self.isConfirmationPresented = true
        }) {
            Text("Remove Profile")
        }
        .foregroundColor(.red)
        .alert(isPresented: $isConfirmationPresented) {
            Alert(
                title: Text("Are you sure you want to remove the profile?"),
                primaryButton: .destructive(Text("Remove profile"), action: {
                    self.isConfirmationPresented = false
                    self.model.buttonRemoveProfileTapped()
                }),
                secondaryButton: .cancel()
            )
        }
    }
}

struct TunnelView_Previews: PreviewProvider {
    static var previews: some View {
        TunnelDetailsView(model: .init(tunnel: .init()))
    }
}


================================================
FILE: vpn-client/Screens/TunnelDetails/TunnelDetailsViewModel.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import SwiftUI
import Combine
import NetworkExtension

final class TunnelViewModel: ObservableObject {
    @Published var username = ""
    @Published var password = ""
    @Published var server = ""

    @Published var isEnabled = false
    @Published var isStarted = false

    @Published private(set) var status: String = "Unknown"

    @Published var isLoading = false
    @Published var isShowingError = false
    @Published private(set) var errorTitle = ""
    @Published private(set) var errorMessage = ""

    private let service: VPNConfigurationService
    private let tunnel: NETunnelProviderManager

    private var observers = [AnyObject]()
    private var bag = [AnyCancellable]()

    init(service: VPNConfigurationService = .shared, tunnel: NETunnelProviderManager) {
        self.service = service
        self.tunnel = tunnel

        self.refresh()

        observers.append(NotificationCenter.default
            .addObserver(forName: .NEVPNStatusDidChange, object: tunnel.connection, queue: .main) { [weak self] _ in
                self?.refresh()
        })

        observers.append(NotificationCenter.default
            .addObserver(forName: .NEVPNConfigurationChange, object: tunnel, queue: .main) { [weak self] _ in
                self?.refresh()
        })

        $isEnabled.sink { [weak self] in
            self?.setEnabled($0)
        }.store(in: &bag)
    }

    private func refresh() {
        self.status = tunnel.connection.status.description
        let username = tunnel.protocolConfiguration?.username ?? ""
        self.username = username
        self.password = tunnel.protocolConfiguration?.passwordReference.flatMap {
            Keychain.password(for: username, reference: $0)
        } ?? ""
        self.server = tunnel.protocolConfiguration?.serverAddress ?? ""
        self.isEnabled = tunnel.isEnabled
        self.isStarted = tunnel.connection.status != .disconnected && tunnel.connection.status != .invalid
    }

    private func setEnabled(_ isEnabled: Bool) {
        guard isEnabled != tunnel.isEnabled else { return }
        tunnel.isEnabled = isEnabled
        saveToPreferences()
    }

    func buttonStartTapped() {
        do {
            try tunnel.connection.startVPNTunnel(options: [:] as [String : NSObject])
        } catch {
            self.showError(title: "Failed to start VPN tunnel", message: error.localizedDescription)
        }
    }

    func buttonStopTapped() {
        tunnel.connection.stopVPNTunnel()
    }

    func buttonRemoveProfileTapped() {
        isLoading = true

        service.removeProfile { [weak self] in
            guard let self = self else { return }
            self.isLoading = false
            switch $0 {
            case .success:
            break // Do nothing, router will show what's next
            case let .failure(error):
                self.showError(title: "Failed to install a profile", message: error.localizedDescription)
            }
        }
    }

    private func saveToPreferences() {
        isLoading = true
        tunnel.saveToPreferences { [weak self] error in
            guard let self = self else { return }
            self.isLoading = false
            if let error = error {
                self.showError(title: "Failed to update VPN configuration", message: error.localizedDescription)
                self.errorMessage = error.localizedDescription
                return
            }
        }
    }

    private func showError(title: String, message: String) {
        self.errorTitle = title
        self.errorMessage = message
        self.isShowingError = true
    }

    func buttonSaveTapped() {
        let proto = tunnel.protocolConfiguration as! NETunnelProviderProtocol
        proto.username = self.username
        proto.passwordReference = {
            let keychain = Keychain(group: "group.com.github.kean.vpn-client")
            keychain.set(password: self.password, for: username)
            return keychain.passwordReference(for: username)
        }()
        proto.serverAddress = server

        tunnel.protocolConfiguration = proto

        saveToPreferences()
    }

    private func sayHelloToTunnel() {
        // Send a simple IPC message to the provider, handle the response.
        guard let session = tunnel.connection as? NETunnelProviderSession,
            let message = "Hello Provider".data(using: String.Encoding.utf8), tunnel.connection.status != .invalid else {
                return
        }

        do {
            try session.sendProviderMessage(message) { response in
                if response != nil {
                    let responseString = NSString(data: response!, encoding: String.Encoding.utf8.rawValue)
                    NSLog("Received response from the provider: \(String(describing: responseString))")
                } else {
                    NSLog("Got a nil response from the provider")
                }
            }
        } catch {
            NSLog("Failed to send a message to the provider")
        }
    }
}


================================================
FILE: vpn-client/Screens/WelcomeView.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import SwiftUI

struct WelcomeView: View {
    let service: VPNConfigurationService = .shared

    @State private var isLoading = false
    @State private var isShowingError = false
    @State private var errorMessage = ""

    var body: some View {
        NavigationView {
            VStack {
                header
                Spacer(minLength: 0)
                buttonInstall
                Spacer().frame(height: 16)
            }.padding()
        }
    }

    private var header: some View {
        VStack {
            Text("BestVPN")
                .font(.largeTitle)
                .fontWeight(.heavy)
            HStack(spacing: 20) {
                Image("icon-vpn")
                    .resizable()
                    .frame(width: 50, height: 50)
                VStack(alignment: .leading) {
                    Text("Custom VPN solution")
                        .font(.headline)
                    Text("Powered by Network Extension and SwiftNIO")
                }
            }
        }
    }

    private var buttonInstall: some View {
        PrimaryButton(
            title: "Install VPN Profile",
            action: self.installProfile,
            isLoading: $isLoading
        ).alert(isPresented: $isShowingError) {
            Alert(
                title: Text("Failed to install a profile"),
                message: Text(errorMessage),
                dismissButton: .cancel()
            )
        }
    }

    private func installProfile() {
        isLoading = true

        service.installProfile { result in
            self.isLoading = false
            switch result {
            case .success:
                break // Do nothing, router will show what's next
            case let .failure(error):
                self.errorMessage = error.localizedDescription
                self.isShowingError = true
            }
        }
    }
}

struct WelcomeView_Previews: PreviewProvider {
    static var previews: some View {
        WelcomeView()
    }
}


================================================
FILE: vpn-client/Services/Keychain.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import Foundation

/// Wrapper for easy keychain access and modification.
public final class Keychain {
    private let accessGroup: String

    public init(group: String) {
        self.accessGroup = group
    }

    @discardableResult
    public func set(password: String, for username: String, label: String? = nil) -> OSStatus {
        removePassword(for: username)

        var query = [String: Any]()
        query[kSecAttrAccessGroup as String] = accessGroup
        query[kSecClass as String] = kSecClassGenericPassword
        if let label = label {
            query[kSecAttrLabel as String] = label
        }
        query[kSecAttrAccount as String] = username
        query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
        query[kSecValueData as String] = password.data(using: .utf8)

        return SecItemAdd(query as CFDictionary, nil)
    }

    @discardableResult
    public func removePassword(for username: String) -> Bool {
        var query = [String: Any]()
        query[kSecAttrAccessGroup as String] = accessGroup
        query[kSecClass as String] = kSecClassGenericPassword
        query[kSecAttrAccount as String] = username

        let status = SecItemDelete(query as CFDictionary)
        return status == errSecSuccess
    }

    public func password(for username: String) throws -> String? {
        var query = [String: Any]()
        query[kSecAttrAccessGroup as String] = accessGroup
        query[kSecClass as String] = kSecClassGenericPassword
        query[kSecAttrAccount as String] = username
        query[kSecMatchLimit as String] = kSecMatchLimitOne
        query[kSecReturnData as String] = true

        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        guard status == errSecSuccess, let data = result as? Data else {
            return nil
        }
        return String(data: data, encoding: .utf8)
    }

    public func passwordReference(for username: String) -> Data? {
        var query = [String: Any]()
        query[kSecAttrAccessGroup as String] = accessGroup
        query[kSecClass as String] = kSecClassGenericPassword
        query[kSecAttrAccount as String] = username
        query[kSecMatchLimit as String] = kSecMatchLimitOne
        query[kSecReturnPersistentRef as String] = true

        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        guard status == errSecSuccess, let data = result as? Data else {
            return nil
        }
        return data
    }

    public static func password(for username: String, reference: Data) -> String? {
        var query = [String: Any]()
        query[kSecClass as String] = kSecClassGenericPassword
        query[kSecAttrAccount as String] = username
        query[kSecMatchItemList as String] = [reference]
        query[kSecReturnData as String] = true

        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        guard status == errSecSuccess, let data = result as? Data else {
            return nil
        }
        return String(data: data, encoding: .utf8)
    }
}


================================================
FILE: vpn-client/Services/VPNConfigurationService.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import Foundation
import Combine
import NetworkExtension
import UIKit

final class VPNConfigurationService: ObservableObject {
    @Published private(set) var isStarted = false

    /// If not nil, the tunnel is displayed.
    @Published private(set) var tunnel: NETunnelProviderManager?

    static let shared = VPNConfigurationService()

    private var observer: AnyObject?

    private init() {
        observer = NotificationCenter.default.addObserver(
            forName: UIApplication.willEnterForegroundNotification,
            object: nil, queue: .main) { [weak self] _ in
                self?.refresh()
        }
    }

    private func refresh() {
        refresh { _ in }
    }

    func refresh(_ completion: @escaping (Result<Void, Error>) -> Void) {
        // Read all of the VPN configurations created by the app that have
        // previously been saved to the Network Extension preferences.
        NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
            guard let self = self else { return }

            // There is only one VPN configuration the app provides
            self.tunnel = managers?.first
            if let error = error {
                completion(.failure(error))
            } else {
                self.isStarted = true
                completion(.success(()))
            }
        }
    }

    func installProfile(_ completion: @escaping (Result<Void, Error>) -> Void) {
        let tunnel = makeManager()
        tunnel.saveToPreferences { [weak self] error in
            if let error = error {
                return completion(.failure(error))
            }

            // See https://forums.developer.apple.com/thread/25928
            tunnel.loadFromPreferences { [weak self] error in
                self?.tunnel = tunnel
                completion(.success(()))
            }
        }
    }

    private func makeManager() -> NETunnelProviderManager {
        let manager = NETunnelProviderManager()
        manager.localizedDescription = "BestVPN"

        let proto = NETunnelProviderProtocol()

        // WARNING: This must match the bundle identifier of the app extension
        // containing packet tunnel provider.
        proto.providerBundleIdentifier = "com.github.kean.vpn-client.vpn-tunnel"

        // WARNING: This must send the actual VPN server address, for the demo
        // purposes, I'm passing the address of the server in my local network.
        // The address is going to be different in your network.
        proto.serverAddress = "192.168.0.13:9999"

        proto.username = "kean"
        proto.passwordReference = {
            let keychain = Keychain(group: "group.com.github.kean.vpn-client")
            keychain.set(password: "123456", for: "kean")
            return keychain.passwordReference(for: "kean")
        }()

        manager.protocolConfiguration = proto

        // Uncomment this to configure on-demand rules to make sure the tunnel
        // starts automatically when needed.
//        let onDemandRule = NEOnDemandRuleConnect()
//        onDemandRule.interfaceTypeMatch = .any
//        manager.isOnDemandEnabled = true
//        manager.onDemandRules = [onDemandRule]

        // Enable the manager bu default.
        manager.isEnabled = true

        return manager
    }

    private func statusUpdated() {

    }
    
    func removeProfile(_ completion: @escaping (Result<Void, Error>) -> Void) {
        assert(tunnel != nil, "Tunnel is missing")
        tunnel?.removeFromPreferences { error in
            if let error = error {
                return completion(.failure(error))
            }
            self.tunnel = nil
            completion(.success(()))
        }
    }
}

// MARK: - Extensions

/// Make NEVPNStatus convertible to a string
extension NEVPNStatus: CustomStringConvertible {
    public var description: String {
        switch self {
        case .disconnected: return "Disconnected"
        case .invalid: return "Invalid"
        case .connected: return "Connected"
        case .connecting: return "Connecting"
        case .disconnecting: return "Disconnecting"
        case .reasserting: return "Reconnecting"
        @unknown default: return "Unknown"
        }
    }
}


================================================
FILE: vpn-client/vpn-client.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.networking.networkextension</key>
	<array>
		<string>packet-tunnel-provider</string>
	</array>
	<key>com.apple.security.application-groups</key>
	<array>
		<string>group.com.github.kean.vpn-client</string>
	</array>
</dict>
</plist>


================================================
FILE: vpn-protocol/Cipher.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import Foundation
import CryptoKit

// WARNING: This is a simplified example, don't use it as a reference.
public enum Cipher {

    /// WARNING: This is simplified example, don't do this in production!
    ///
    /// A "pre-shared" symmetric key.
    public static let key = SymmetricKey(data: Data(base64Encoded: "KGiEbfJODclOCzUVfWXBO7Y/ohnEVxf7+RnwaAA1/78=")!)

    // WARNING: This is a simplified example, don't use it as a reference.
    public static func encrypt(_ data: Data, key: SymmetricKey) throws -> Data {
        try ChaChaPoly.seal(data, using: key).combined
    }

    // WARNING: This is a simplified example, don't use it as a reference.
    public static func decrypt(_ data: Data, key: SymmetricKey) throws -> Data {
        try ChaChaPoly.open(ChaChaPoly.SealedBox(combined: data), using: key)
    }
}

public extension SymmetricKey {
    /// A Data instance created safely from the contiguous bytes without making any copies.
    var dataRepresentation: Data {
        return self.withUnsafeBytes { bytes in
            let cfdata = CFDataCreateWithBytesNoCopy(nil, bytes.baseAddress?.assumingMemoryBound(to: UInt8.self), bytes.count, kCFAllocatorNull)
            return ((cfdata as NSData?) as Data?) ?? Data()
        }
    }
}


================================================
FILE: vpn-protocol/Packet.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import Foundation
import CryptoKit

public enum PacketCode: UInt8 {
    /// A control packet containing client authentication request (JSON).
    case clientAuthRequest = 0x01
    /// A control packet containing server authentication response (JSON).
    case serverAuthResponse = 0x02
    /// A data packet containing encrypted IP packets (raw bytes).
    case data = 0x03

    /// Initilizes the code code with the given UDP packet contents.
    public init(datagram: Data) throws {
        guard datagram.count > 0 else {
            throw PacketParsingError.notEnoughData
        }
        guard let code = PacketCode(rawValue: datagram[0]) else {
            throw PacketParsingError.invalidPacketCode
        }
        self = code
    }
}

public enum PacketParsingError: Error {
    case notEnoughData
    case invalidPacketCode
}

public struct Header {
    public let code: PacketCode

    public static let length = 1

    public init(code: PacketCode) {
        self.code = code
    }
}

public enum Body {
    public struct ClientAuthRequest: Codable {
        public let login: String
        public let password: String

        public init(login: String, password: String) {
            self.login = login
            self.password = password
        }
    }

    public struct ServerAuthResponse: Codable {
        public let isOK: Bool

        public init(isOK: Bool) {
            self.isOK = isOK
        }
    }

    public typealias Data = Foundation.Data
}

// MARK: - Encoder

public enum MessageEncoder {
    public static func encode<Body: Codable>(header: Header, body: Body, key: SymmetricKey) throws -> Data {
        try encode(header: header, body: JSONEncoder().encode(body), key: key)
    }

    public static func encode(header: Header, body: Data, key: SymmetricKey) throws -> Data {
        var data = Data()

        // Header
        data.append(header.code.rawValue)

        // Body
        let body = try Cipher.encrypt(body, key: key)
        data.append(body)

        return data
    }
}

public enum MessageDecoder {
    /// Decrypts and decodes the body of the given datagram.
    public static func decode<T: Decodable>(_ type: T.Type, datagram: Data, key: SymmetricKey) throws -> T {
        let data = try Cipher.decrypt(datagram[Header.length...], key: key)
        return try JSONDecoder().decode(type, from: data)
    }
}


================================================
FILE: vpn-protocol/Session.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import Foundation
import CryptoKit

// WARNING: This is a simplified example, don't use it as a reference.
public final class Cipher {
    private let key: SymmetricKey

    public init(key: SymmetricKey) {
        self.key = key
    }

    // WARNING: This is a simplified example, don't use it as a reference.
    public func encrypt(_ data: Data) throws -> Data {
        try ChaChaPoly.seal(data, using: key).combined
    }

    // WARNING: This is a simplified example, don't use it as a reference.
    public func decrypt(_ data: Data) throws -> Data {
        try ChaChaPoly.open(ChaChaPoly.SealedBox(combined: data), using: key)
    }
}


================================================
FILE: vpn-server/main.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import Foundation
import NIO
import BestVPN

private final class VPNDatagramHandler: ChannelInboundHandler {
    public typealias InboundIn = AddressedEnvelope<ByteBuffer>
    public typealias OutboundOut = AddressedEnvelope<ByteBuffer>

    public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
        let inputEnvelope = unwrapInboundIn(data)
        let inputData = Data(inputEnvelope.data.readableBytesView) // TODO: this probably isn't optimal
        do {
            let code = try PacketCode(datagram: inputData)

            switch code {
            case .clientAuthRequest:
                let outputData = try MessageEncoder.encode(
                    header: Header(code: .serverAuthResponse), body: Body.ServerAuthResponse(isOK: true), key: Cipher.key
                )

                var buffer = context.channel.allocator.buffer(capacity: outputData.count)
                buffer.writeBytes(outputData)

                let outputEnvelope = AddressedEnvelope(remoteAddress: inputEnvelope.remoteAddress, data: buffer)
                context.write(wrapOutboundOut(outputEnvelope), promise: nil)

            case .data:
                // TODO: Inject packets into a virtual interface
                break
            default:
                break
            }
        } catch {
            // TODO: Handle errors
        }
    }

    public func channelReadComplete(context: ChannelHandlerContext) {
        // As we are not really interested getting notified on success or failure we just pass nil as promise to
        // reduce allocations.
        context.flush()
    }

    public func errorCaught(context: ChannelHandlerContext, error: Error) {
        print("error: ", error)

        // As we are not really interested getting notified on success or failure we just pass nil as promise to
        // reduce allocations.
        context.close(promise: nil)
    }
}

// We don't need more than one thread, as we're creating only one datagram channel.
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
var bootstrap = DatagramBootstrap(group: group)
    // Specify backlog and enable SO_REUSEADDR
    .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)

    // Set the handlers that are applied to the bound channel
    .channelInitializer { channel in
        // Ensure we don't read faster than we can write by adding the BackPressureHandler into the pipeline.
        channel.pipeline.addHandler(VPNDatagramHandler())
    }

defer {
    try! group.syncShutdownGracefully()
}

var arguments = CommandLine.arguments.dropFirst(0) // just to get an ArraySlice<String> from [String]
if arguments.dropFirst().first == .some("--enable-gathering-reads") {
    bootstrap = bootstrap.channelOption(ChannelOptions.datagramVectorReadMessageCount, value: 30)
    bootstrap = bootstrap.channelOption(ChannelOptions.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 30 * 2048))
    arguments = arguments.dropFirst()
}
let arg1 = arguments.dropFirst().first
let arg2 = arguments.dropFirst().dropFirst().first

#warning("TEMP:")
//let defaultHost = "::1"
let defaultHost = "192.168.0.13"
let defaultPort = 9999

enum BindTo {
    case ip(host: String, port: Int)
    case unixDomainSocket(path: String)
}

let bindTarget: BindTo
switch (arg1, arg1.flatMap(Int.init), arg2.flatMap(Int.init)) {
case (.some(let h), _ , .some(let p)):
    /* we got two arguments, let's interpret that as host and port */
    bindTarget = .ip(host: h, port: p)
case (.some(let portString), .none, _):
    /* couldn't parse as number, expecting unix domain socket path */
    bindTarget = .unixDomainSocket(path: portString)
case (_, .some(let p), _):
    /* only one argument --> port */
    bindTarget = .ip(host: defaultHost, port: p)
default:
    bindTarget = .ip(host: defaultHost, port: defaultPort)
}

let channel = try { () -> Channel in
    switch bindTarget {
    case .ip(let host, let port):
        return try bootstrap.bind(host: host, port: port).wait()
    case .unixDomainSocket(let path):
        return try bootstrap.bind(unixDomainSocketPath: path).wait()
    }
}()

print("Server started and listening on \(channel.localAddress!)")

// This will never unblock as we don't close the channel
try channel.closeFuture.wait()

print("Server closed")



================================================
FILE: vpn-tunnel/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleDisplayName</key>
	<string>vpn-tunnel</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>NSExtension</key>
	<dict>
		<key>NSExtensionPointIdentifier</key>
		<string>com.apple.networkextension.packet-tunnel</string>
		<key>NSExtensionPrincipalClass</key>
		<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
	</dict>
</dict>
</plist>


================================================
FILE: vpn-tunnel/PacketTunnelProvider.swift
================================================
// The MIT License (MIT)
//
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).

import NetworkExtension
import BestVPN
import CryptoKit
import os.log

class PacketTunnelProvider: NEPacketTunnelProvider {

    private var configuration: Configuration!
    private var udpSession: NWUDPSession!
    private var key: SymmetricKey!
    private var observer: AnyObject?
    #warning("TODO: do we need this queue?")
    private let queue = DispatchQueue(label: "com.github.packet-tunnel-provider")
    private let log = OSLog(subsystem: "vpn-tunnel-ptp", category: "default")
    private var pendingCompletion: ((Error?) -> Void)?
    private weak var timeoutTimer: Timer?

    override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
        os_log(.default, log: log, "Starting tunnel, options: %{private}@", "\(String(describing: options))")

        do {
            guard let proto = protocolConfiguration as? NETunnelProviderProtocol else {
                throw NEVPNError(.configurationInvalid)
            }
            self.configuration = try Configuration(proto: proto)
        } catch {
            os_log(.error, log: log, "Failed to read the configuration", error.localizedDescription)
            completionHandler(error)
        }

        os_log(.default, log: log, "Read configuration %{private}@", "\(String(describing: configuration))")

        // "Get" a pre-shared symmetric key.
        //
        // WARNING: Don't do this in production! This code uses the same key
        // every time for simplicity. In practice, you are going to want to
        // either pre-share it properly, or implement proper TLS handshake.
        self.key = Cipher.key

        // Remember the completion handler so that we could call it when the
        // the connection with the server is established.
        //
        // When the Packet Tunnel Provider executes the completionHandler block
        // with a nil error parameter, it signals to the system that it is ready
        // to begin handling network data. Therefore, the Packet Tunnel Provider
        // should call `setTunnelNetworkSettings(_:completionHandler:)` and wait
        // for it to complete before executing the completionHandler block.
        //
        // The domain and code of the NSError object passed to the completionHandler
        // block are defined by the Packet Tunnel Provider (`NEVPNError`).
        self.pendingCompletion = completionHandler

        self.startTunnel()
    }

    private func startTunnel() {
        self.startUDPSession()

        self.timeoutTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { [weak self] _ in
            guard let self = self else { return }

            self.pendingCompletion?(NEVPNError(.connectionFailed))
            self.pendingCompletion = nil
        }
    }

    private func startUDPSession() {
        os_log(.default, log: log, "Starting UDP session, hostname: %{public}@, port: %{public}@", configuration.hostname, configuration.port)

        let endpoint = NWHostEndpoint(hostname: configuration.hostname, port: configuration.port)
        self.udpSession = createUDPSession(to: endpoint, from: nil)
        self.observer = udpSession.observe(\.state, options: [.new]) { [weak self] session, _ in
            guard let self = self else { return }
            os_log(.default, log: self.log, "Session did update state: %{public}@", session.state.description)
            self.queue.async {
                self.udpSession(session, didUpdateState: session.state)
            }
        }
    }

    private func udpSession(_ session: NWUDPSession, didUpdateState state: NWUDPSessionState) {
        switch state {
        case .ready:
            guard pendingCompletion != nil else { return }

            session.setReadHandler({ [weak self] datagrams, error in
                guard let self = self else { return }
                self.queue.async {
                    self.didReceiveDatagrams(datagrams: datagrams ?? [], error: error)
                }
            }, maxDatagrams: Int.max)

            do {
                try self.authenticate(username: configuration.username, password: configuration.password)
            } catch {
                // TODO: handle errors
                os_log(.default, log: self.log, "Did fail to authenticate: %{public}@", "\(error)")
            }
        case .failed:
            guard pendingCompletion != nil else { return }
            pendingCompletion?(NEVPNError(.connectionFailed))
            pendingCompletion = nil
        default:
            break
        }
    }

    private func didReceiveDatagrams(datagrams: [Data], error: Error?) {
        for datagram in datagrams {
            do {
                try self.didReceiveDatagram(datagram: datagram)
            } catch {
                // TODO: handle error
                os_log(.default, log: self.log, "UDP session read handler error: %{public}@", "\(error)")
            }
        }
        if let error = error {
            // TODO: handle error
            os_log(.default, log: self.log, "UDP session read handler error: %{public}@", "\(error)")
        }
    }

    private func didReceiveDatagram(datagram: Data) throws {
        let code = try PacketCode(datagram: datagram)

        os_log(.default, log: self.log, "Did receive datagram with code: %{public}@", "\(code)")

        switch code {
        case .serverAuthResponse:
            let response = try MessageDecoder.decode(Body.ServerAuthResponse.self, datagram: datagram, key: key)
            os_log(.default, log: self.log, "Did receive auth response: %{private}@", "\(response)")
            if response.isOK {
                // TODO: In reality, you would pass a resolved IP address, in our
                // case we already provide an IP address in the configurtaion
                self.didSetupTunnel(address: configuration.hostname)
            } else {
                // TODO: Handle error
            }
            self.timeoutTimer?.invalidate()
        case .data:
            let data = try MessageDecoder.decode(Data.self, datagram: datagram, key: key)
            let proto = protocolNumber(for: data)
            self.packetFlow.writePackets([data], withProtocols: [proto])
        default:
            break
        }
    }

    private func didSetupTunnel(address: String) {
        os_log(.default, log: self.log, "Did setup tunnel with address: %{public}@", "\(address)")

        let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: address)
        // TODO: Configure DNS/split-tunnel/etc settings if needed

        setTunnelNetworkSettings(settings) { error in
            os_log(.default, log: self.log, "Did setup tunnel settings: %{public}@, error: %{public}@", "\(settings)", "\(String(describing: error))")

            self.pendingCompletion?(error)
            self.pendingCompletion = nil

            self.didStartTunnel()
        }
    }

    private func authenticate(username: String, password: String) throws {
        let datagram = try MessageEncoder.encode(
            header: Header(code: .clientAuthRequest),
            body: Body.ClientAuthRequest(login: username, password: password),
            key: key
        )

        udpSession.writeDatagram(datagram) { error in
            if let error = error {
                // TODO: Handle errors
                os_log(.default, log: self.log, "Failed to write auth request datagram, error: %{public}@", "\(error)")
            }
        }
    }

    private func didStartTunnel() {
        readPackets()
    }

    private func readPackets() {
        packetFlow.readPacketObjects { packets in
            do {
                let datagrams = try packets.map {
                    try MessageEncoder.encode(
                        header: Header(code: .data),
                        body: $0.data,
                        key: key
                    )
                }
                self.session.writeMultipleDatagrams(datagrams) { error in
                    // TODO: Handle errors
                }
            } catch {
                // TODO: Handle errors
            }

            self.readPackets()
        }
    }

    override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        // Add code here to start the process of stopping the tunnel.
        completionHandler()
    }
    
    override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
        // Add code here to handle the message.
        if let handler = completionHandler {
            handler(messageData)
        }
    }
    
    override func sleep(completionHandler: @escaping () -> Void) {
        // Add code here to get ready to sleep.
        completionHandler()
    }
    
    override func wake() {
        // Add code here to wake up.
    }
}

private struct Configuration {
    let username: String
    let password: String
    let hostname: String
    let port: String

    init(proto: NETunnelProviderProtocol) throws {
        guard let fullServerAddress = proto.serverAddress else {
            throw NEVPNError(.configurationInvalid)
        }
        let serverAddressParts = fullServerAddress.split(separator: ":")
        guard serverAddressParts.count == 2 else {
            throw NEVPNError(.configurationInvalid)
        }

        self.hostname = String(serverAddressParts[0])
        self.port = String(serverAddressParts[1])

        guard let username = proto.username else {
            throw NEVPNError(.configurationInvalid)
        }
        self.username = username

        guard let password = proto.passwordReference.flatMap({
            Keychain.password(for: username, reference: $0)
        }) else {
            throw NEVPNError(.configurationInvalid)
        }
        self.password = password
    }
}

extension NWUDPSessionState: CustomStringConvertible {
    public var description: String {
        switch self {
        case .cancelled: return ".cancelled"
        case .failed: return ".failed"
        case .invalid: return ".invalid"
        case .preparing: return ".preparing"
        case .ready: return ".ready"
        case .waiting: return ".waiting"
        @unknown default: return "unknown"
        }
    }
}

private func protocolNumber(for packet: Data) -> NSNumber {
    guard !packet.isEmpty else {
        return AF_INET as NSNumber
    }

    // 'packet' contains the decrypted incoming IP packet data

    // The first 4 bits identify the IP version
    let ipVersion = (packet[0] & 0xf0) >> 4
    return (ipVersion == 6) ? AF_INET6 as NSNumber : AF_INET as NSNumber
}


================================================
FILE: vpn-tunnel/vpn_tunnel.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.networking.networkextension</key>
	<array>
		<string>packet-tunnel-provider</string>
	</array>
	<key>com.apple.security.application-groups</key>
	<array>
		<string>group.com.github.kean.vpn-client</string>
	</array>
</dict>
</plist>


================================================
FILE: vpn.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 52;
	objects = {

/* Begin PBXBuildFile section */
		0C189C482479D399000EE6B3 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C189C432479D280000EE6B3 /* main.swift */; };
		0C230CDA24884C35005FEEBD /* libBestVPN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CD38AF92486C12E00DB2626 /* libBestVPN.a */; };
		0C34DF93248D50E500FBBD11 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C34DF92248D50E500FBBD11 /* Cipher.swift */; };
		0C3C1B5F247039EB00571084 /* VPNConfigurationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3C1B5E247039EB00571084 /* VPNConfigurationService.swift */; };
		0C3C1B6124703B5E00571084 /* TunnelDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3C1B6024703B5E00571084 /* TunnelDetailsView.swift */; };
		0C3C1B6524703D8600571084 /* PrimaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3C1B6424703D8600571084 /* PrimaryButton.swift */; };
		0C3C1B692470404A00571084 /* Spinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3C1B682470404A00571084 /* Spinner.swift */; };
		0C405BED2466FBFE00EB0786 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C405BEC2466FBFE00EB0786 /* AppDelegate.swift */; };
		0C405BEF2466FBFE00EB0786 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C405BEE2466FBFE00EB0786 /* SceneDelegate.swift */; };
		0C405BF12466FBFE00EB0786 /* RouterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C405BF02466FBFE00EB0786 /* RouterView.swift */; };
		0C405BF32466FC0000EB0786 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C405BF22466FC0000EB0786 /* Assets.xcassets */; };
		0C405BF62466FC0000EB0786 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C405BF52466FC0000EB0786 /* Preview Assets.xcassets */; };
		0C405BF92466FC0000EB0786 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C405BF72466FC0000EB0786 /* LaunchScreen.storyboard */; };
		0C571CBA247050E5006B2931 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C571CB9247050E5006B2931 /* NetworkExtension.framework */; };
		0C571CBC24705809006B2931 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C571CBB24705809006B2931 /* SplashView.swift */; };
		0C571CC42470B4D9006B2931 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C571CB9247050E5006B2931 /* NetworkExtension.framework */; };
		0C5F7022248DA579000297AB /* libBestVPN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CD38AF92486C12E00DB2626 /* libBestVPN.a */; };
		0CA5FEBD2491B4B400C71D25 /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA5FEBC2491B4B400C71D25 /* Keychain.swift */; };
		0CA5FEBE2491B4B400C71D25 /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA5FEBC2491B4B400C71D25 /* Keychain.swift */; };
		0CA5FEC12491B72E00C71D25 /* TunnelDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA5FEC02491B72E00C71D25 /* TunnelDetailsViewModel.swift */; };
		0CAE93DD246F4927007B2E95 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAE93DC246F4927007B2E95 /* PacketTunnelProvider.swift */; };
		0CAE93E2246F4927007B2E95 /* vpn-tunnel.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0CAE93DA246F4927007B2E95 /* vpn-tunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
		0CAE940724702557007B2E95 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CAE940624702557007B2E95 /* WelcomeView.swift */; };
		0CD38AFC2486C12E00DB2626 /* Packet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD38AFB2486C12E00DB2626 /* Packet.swift */; };
		0CD38B06248735A700DB2626 /* NIO in Frameworks */ = {isa = PBXBuildFile; productRef = 0CD38B05248735A700DB2626 /* NIO */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		0C230CDB24884C35005FEEBD /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 0C405BE12466FBFE00EB0786 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 0CD38AF82486C12E00DB2626;
			remoteInfo = "vpn-protocol";
		};
		0C5F7023248DA579000297AB /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 0C405BE12466FBFE00EB0786 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 0CD38AF82486C12E00DB2626;
			remoteInfo = "vpn-protocol";
		};
		0CAE93E0246F4927007B2E95 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 0C405BE12466FBFE00EB0786 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 0CAE93D9246F4927007B2E95;
			remoteInfo = "vpn-tunnel";
		};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
		0C189C3F2479D280000EE6B3 /* CopyFiles */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = /usr/share/man/man1/;
			dstSubfolderSpec = 0;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 1;
		};
		0CAE93D5246F4906007B2E95 /* Embed App Extensions */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "";
			dstSubfolderSpec = 13;
			files = (
				0CAE93E2246F4927007B2E95 /* vpn-tunnel.appex in Embed App Extensions */,
			);
			name = "Embed App Extensions";
			runOnlyForDeploymentPostprocessing = 0;
		};
		0CD38AF72486C12E00DB2626 /* CopyFiles */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "include/$(PRODUCT_NAME)";
			dstSubfolderSpec = 16;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		0C189C412479D280000EE6B3 /* vpn-server */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "vpn-server"; sourceTree = BUILT_PRODUCTS_DIR; };
		0C189C432479D280000EE6B3 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = main.swift; path = "vpn-server/main.swift"; sourceTree = SOURCE_ROOT; };
		0C34DF92248D50E500FBBD11 /* Cipher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = "<group>"; };
		0C3C1B5D24702E1C00571084 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
		0C3C1B5E247039EB00571084 /* VPNConfigurationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNConfigurationService.swift; sourceTree = "<group>"; };
		0C3C1B6024703B5E00571084 /* TunnelDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelDetailsView.swift; sourceTree = "<group>"; };
		0C3C1B6424703D8600571084 /* PrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryButton.swift; sourceTree = "<group>"; };
		0C3C1B682470404A00571084 /* Spinner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Spinner.swift; sourceTree = "<group>"; };
		0C405BE92466FBFE00EB0786 /* vpn-client.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "vpn-client.app"; sourceTree = BUILT_PRODUCTS_DIR; };
		0C405BEC2466FBFE00EB0786 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
		0C405BEE2466FBFE00EB0786 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
		0C405BF02466FBFE00EB0786 /* RouterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterView.swift; sourceTree = "<group>"; };
		0C405BF22466FC0000EB0786 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		0C405BF52466FC0000EB0786 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
		0C405BF82466FC0000EB0786 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
		0C405BFA2466FC0000EB0786 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		0C571CB7247050E5006B2931 /* vpn-client.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "vpn-client.entitlements"; sourceTree = "<group>"; };
		0C571CB9247050E5006B2931 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
		0C571CBB24705809006B2931 /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
		0C571CC22470B343006B2931 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
		0CA5FEBC2491B4B400C71D25 /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
		0CA5FEC02491B72E00C71D25 /* TunnelDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelDetailsViewModel.swift; sourceTree = "<group>"; };
		0CAE93DA246F4927007B2E95 /* vpn-tunnel.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "vpn-tunnel.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
		0CAE93DC246F4927007B2E95 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
		0CAE93DE246F4927007B2E95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		0CAE93DF246F4927007B2E95 /* vpn_tunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = vpn_tunnel.entitlements; sourceTree = "<group>"; };
		0CAE940624702557007B2E95 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
		0CD38AF92486C12E00DB2626 /* libBestVPN.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBestVPN.a; sourceTree = BUILT_PRODUCTS_DIR; };
		0CD38AFB2486C12E00DB2626 /* Packet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Packet.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		0C189C3E2479D280000EE6B3 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				0C5F7022248DA579000297AB /* libBestVPN.a in Frameworks */,
				0CD38B06248735A700DB2626 /* NIO in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		0C405BE62466FBFE00EB0786 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				0C571CBA247050E5006B2931 /* NetworkExtension.framework in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		0CAE93D7246F4927007B2E95 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				0C230CDA24884C35005FEEBD /* libBestVPN.a in Frameworks */,
				0C571CC42470B4D9006B2931 /* NetworkExtension.framework in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		0CD38AF62486C12E00DB2626 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		0C189C422479D280000EE6B3 /* vpn-server */ = {
			isa = PBXGroup;
			children = (
				0C189C432479D280000EE6B3 /* main.swift */,
			);
			path = "vpn-server";
			sourceTree = "<group>";
		};
		0C3C1B662470403600571084 /* Screens */ = {
			isa = PBXGroup;
			children = (
				0CA5FEBF2491B71F00C71D25 /* TunnelDetails */,
				0CAE940624702557007B2E95 /* WelcomeView.swift */,
				0C405BF02466FBFE00EB0786 /* RouterView.swift */,
				0C3C1B6424703D8600571084 /* PrimaryButton.swift */,
				0C3C1B682470404A00571084 /* Spinner.swift */,
				0C571CBB24705809006B2931 /* SplashView.swift */,
			);
			path = Screens;
			sourceTree = "<group>";
		};
		0C3C1B672470403C00571084 /* Services */ = {
			isa = PBXGroup;
			children = (
				0C3C1B5E247039EB00571084 /* VPNConfigurationService.swift */,
				0CA5FEBC2491B4B400C71D25 /* Keychain.swift */,
			);
			path = Services;
			sourceTree = "<group>";
		};
		0C405BE02466FBFE00EB0786 = {
			isa = PBXGroup;
			children = (
				0C3C1B5D24702E1C00571084 /* README.md */,
				0C405BEB2466FBFE00EB0786 /* vpn-client */,
				0CAE93DB246F4927007B2E95 /* vpn-tunnel */,
				0C189C422479D280000EE6B3 /* vpn-server */,
				0CD38AFA2486C12E00DB2626 /* vpn-protocol */,
				0C405BEA2466FBFE00EB0786 /* Products */,
				0C571CB8247050E5006B2931 /* Frameworks */,
			);
			sourceTree = "<group>";
		};
		0C405BEA2466FBFE00EB0786 /* Products */ = {
			isa = PBXGroup;
			children = (
				0C405BE92466FBFE00EB0786 /* vpn-client.app */,
				0CAE93DA246F4927007B2E95 /* vpn-tunnel.appex */,
				0C189C412479D280000EE6B3 /* vpn-server */,
				0CD38AF92486C12E00DB2626 /* libBestVPN.a */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		0C405BEB2466FBFE00EB0786 /* vpn-client */ = {
			isa = PBXGroup;
			children = (
				0C571CB7247050E5006B2931 /* vpn-client.entitlements */,
				0C405BEC2466FBFE00EB0786 /* AppDelegate.swift */,
				0C405BEE2466FBFE00EB0786 /* SceneDelegate.swift */,
				0C3C1B672470403C00571084 /* Services */,
				0C3C1B662470403600571084 /* Screens */,
				0C405BF22466FC0000EB0786 /* Assets.xcassets */,
				0C405BF72466FC0000EB0786 /* LaunchScreen.storyboard */,
				0C405BFA2466FC0000EB0786 /* Info.plist */,
				0C405BF42466FC0000EB0786 /* Preview Content */,
			);
			path = "vpn-client";
			sourceTree = "<group>";
		};
		0C405BF42466FC0000EB0786 /* Preview Content */ = {
			isa = PBXGroup;
			children = (
				0C405BF52466FC0000EB0786 /* Preview Assets.xcassets */,
			);
			path = "Preview Content";
			sourceTree = "<group>";
		};
		0C571CB8247050E5006B2931 /* Frameworks */ = {
			isa = PBXGroup;
			children = (
				0C571CC22470B343006B2931 /* libresolv.tbd */,
				0C571CB9247050E5006B2931 /* NetworkExtension.framework */,
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
		0CA5FEBF2491B71F00C71D25 /* TunnelDetails */ = {
			isa = PBXGroup;
			children = (
				0C3C1B6024703B5E00571084 /* TunnelDetailsView.swift */,
				0CA5FEC02491B72E00C71D25 /* TunnelDetailsViewModel.swift */,
			);
			path = TunnelDetails;
			sourceTree = "<group>";
		};
		0CAE93DB246F4927007B2E95 /* vpn-tunnel */ = {
			isa = PBXGroup;
			children = (
				0CAE93DC246F4927007B2E95 /* PacketTunnelProvider.swift */,
				0CAE93DE246F4927007B2E95 /* Info.plist */,
				0CAE93DF246F4927007B2E95 /* vpn_tunnel.entitlements */,
			);
			path = "vpn-tunnel";
			sourceTree = "<group>";
		};
		0CD38AFA2486C12E00DB2626 /* vpn-protocol */ = {
			isa = PBXGroup;
			children = (
				0CD38AFB2486C12E00DB2626 /* Packet.swift */,
				0C34DF92248D50E500FBBD11 /* Cipher.swift */,
			);
			path = "vpn-protocol";
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		0C189C402479D280000EE6B3 /* vpn-server */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 0C189C452479D280000EE6B3 /* Build configuration list for PBXNativeTarget "vpn-server" */;
			buildPhases = (
				0C189C3D2479D280000EE6B3 /* Sources */,
				0C189C3E2479D280000EE6B3 /* Frameworks */,
				0C189C3F2479D280000EE6B3 /* CopyFiles */,
			);
			buildRules = (
			);
			dependencies = (
				0C5F7024248DA579000297AB /* PBXTargetDependency */,
			);
			name = "vpn-server";
			packageProductDependencies = (
				0CD38B05248735A700DB2626 /* NIO */,
			);
			productName = "vpn-server";
			productReference = 0C189C412479D280000EE6B3 /* vpn-server */;
			productType = "com.apple.product-type.tool";
		};
		0C405BE82466FBFE00EB0786 /* vpn-client */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 0C405BFD2466FC0100EB0786 /* Build configuration list for PBXNativeTarget "vpn-client" */;
			buildPhases = (
				0C405BE52466FBFE00EB0786 /* Sources */,
				0C405BE62466FBFE00EB0786 /* Frameworks */,
				0C405BE72466FBFE00EB0786 /* Resources */,
				0CAE93D5246F4906007B2E95 /* Embed App Extensions */,
			);
			buildRules = (
			);
			dependencies = (
				0CAE93E1246F4927007B2E95 /* PBXTargetDependency */,
			);
			name = "vpn-client";
			productName = VPNClient;
			productReference = 0C405BE92466FBFE00EB0786 /* vpn-client.app */;
			productType = "com.apple.product-type.application";
		};
		0CAE93D9246F4927007B2E95 /* vpn-tunnel */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 0CAE93E3246F4927007B2E95 /* Build configuration list for PBXNativeTarget "vpn-tunnel" */;
			buildPhases = (
				0CAE93D6246F4927007B2E95 /* Sources */,
				0CAE93D7246F4927007B2E95 /* Frameworks */,
				0CAE93D8246F4927007B2E95 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				0C230CDC24884C35005FEEBD /* PBXTargetDependency */,
			);
			name = "vpn-tunnel";
			productName = "vpn-tunnel";
			productReference = 0CAE93DA246F4927007B2E95 /* vpn-tunnel.appex */;
			productType = "com.apple.product-type.app-extension";
		};
		0CD38AF82486C12E00DB2626 /* vpn-protocol */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 0CD38AFD2486C12E00DB2626 /* Build configuration list for PBXNativeTarget "vpn-protocol" */;
			buildPhases = (
				0CD38AF52486C12E00DB2626 /* Sources */,
				0CD38AF62486C12E00DB2626 /* Frameworks */,
				0CD38AF72486C12E00DB2626 /* CopyFiles */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = "vpn-protocol";
			productName = "vpn-protocol";
			productReference = 0CD38AF92486C12E00DB2626 /* libBestVPN.a */;
			productType = "com.apple.product-type.library.static";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		0C405BE12466FBFE00EB0786 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastSwiftUpdateCheck = 1150;
				LastUpgradeCheck = 1140;
				ORGANIZATIONNAME = kean;
				TargetAttributes = {
					0C189C402479D280000EE6B3 = {
						CreatedOnToolsVersion = 11.5;
					};
					0C405BE82466FBFE00EB0786 = {
						CreatedOnToolsVersion = 11.4.1;
					};
					0CAE93D9246F4927007B2E95 = {
						CreatedOnToolsVersion = 11.4.1;
					};
					0CD38AF82486C12E00DB2626 = {
						CreatedOnToolsVersion = 11.5;
					};
				};
			};
			buildConfigurationList = 0C405BE42466FBFE00EB0786 /* Build configuration list for PBXProject "vpn" */;
			compatibilityVersion = "Xcode 9.3";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 0C405BE02466FBFE00EB0786;
			packageReferences = (
				0CD38B04248735A700DB2626 /* XCRemoteSwiftPackageReference "swift-nio" */,
			);
			productRefGroup = 0C405BEA2466FBFE00EB0786 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				0C405BE82466FBFE00EB0786 /* vpn-client */,
				0CAE93D9246F4927007B2E95 /* vpn-tunnel */,
				0C189C402479D280000EE6B3 /* vpn-server */,
				0CD38AF82486C12E00DB2626 /* vpn-protocol */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		0C405BE72466FBFE00EB0786 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				0C405BF92466FC0000EB0786 /* LaunchScreen.storyboard in Resources */,
				0C405BF62466FC0000EB0786 /* Preview Assets.xcassets in Resources */,
				0C405BF32466FC0000EB0786 /* Assets.xcassets in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		0CAE93D8246F4927007B2E95 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		0C189C3D2479D280000EE6B3 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				0C189C482479D399000EE6B3 /* main.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		0C405BE52466FBFE00EB0786 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				0C405BED2466FBFE00EB0786 /* AppDelegate.swift in Sources */,
				0C405BEF2466FBFE00EB0786 /* SceneDelegate.swift in Sources */,
				0C3C1B6124703B5E00571084 /* TunnelDetailsView.swift in Sources */,
				0CA5FEC12491B72E00C71D25 /* TunnelDetailsViewModel.swift in Sources */,
				0C3C1B5F247039EB00571084 /* VPNConfigurationService.swift in Sources */,
				0C405BF12466FBFE00EB0786 /* RouterView.swift in Sources */,
				0CAE940724702557007B2E95 /* WelcomeView.swift in Sources */,
				0C3C1B6524703D8600571084 /* PrimaryButton.swift in Sources */,
				0C3C1B692470404A00571084 /* Spinner.swift in Sources */,
				0C571CBC24705809006B2931 /* SplashView.swift in Sources */,
				0CA5FEBD2491B4B400C71D25 /* Keychain.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		0CAE93D6246F4927007B2E95 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				0CAE93DD246F4927007B2E95 /* PacketTunnelProvider.swift in Sources */,
				0CA5FEBE2491B4B400C71D25 /* Keychain.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		0CD38AF52486C12E00DB2626 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				0C34DF93248D50E500FBBD11 /* Cipher.swift in Sources */,
				0CD38AFC2486C12E00DB2626 /* Packet.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		0C230CDC24884C35005FEEBD /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 0CD38AF82486C12E00DB2626 /* vpn-protocol */;
			targetProxy = 0C230CDB24884C35005FEEBD /* PBXContainerItemProxy */;
		};
		0C5F7024248DA579000297AB /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 0CD38AF82486C12E00DB2626 /* vpn-protocol */;
			targetProxy = 0C5F7023248DA579000297AB /* PBXContainerItemProxy */;
		};
		0CAE93E1246F4927007B2E95 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 0CAE93D9246F4927007B2E95 /* vpn-tunnel */;
			targetProxy = 0CAE93E0246F4927007B2E95 /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
		0C405BF72466FC0000EB0786 /* LaunchScreen.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				0C405BF82466FC0000EB0786 /* Base */,
			);
			name = LaunchScreen.storyboard;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		0C189C462479D280000EE6B3 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_STYLE = Automatic;
				MACOSX_DEPLOYMENT_TARGET = 10.15;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SDKROOT = macosx;
				SWIFT_VERSION = 5.0;
			};
			name = Debug;
		};
		0C189C472479D280000EE6B3 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_STYLE = Automatic;
				MACOSX_DEPLOYMENT_TARGET = 10.15;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SDKROOT = macosx;
				SWIFT_VERSION = 5.0;
			};
			name = Release;
		};
		0C405BFB2466FC0100EB0786 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
				MTL_FAST_MATH = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
			};
			name = Debug;
		};
		0C405BFC2466FC0100EB0786 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				MTL_FAST_MATH = YES;
				SDKROOT = iphoneos;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};
		0C405BFE2466FC0100EB0786 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_ENTITLEMENTS = "vpn-client/vpn-client.entitlements";
				CODE_SIGN_STYLE = Automatic;
				DEVELOPMENT_ASSET_PATHS = "\"vpn-client/Preview Content\"";
				DEVELOPMENT_TEAM = NR8DLKJ7E6;
				ENABLE_PREVIEWS = YES;
				INFOPLIST_FILE = "vpn-client/Info.plist";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.vpn-client";
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		0C405BFF2466FC0100EB0786 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_ENTITLEMENTS = "vpn-client/vpn-client.entitlements";
				CODE_SIGN_STYLE = Automatic;
				DEVELOPMENT_ASSET_PATHS = "\"vpn-client/Preview Content\"";
				DEVELOPMENT_TEAM = NR8DLKJ7E6;
				ENABLE_PREVIEWS = YES;
				INFOPLIST_FILE = "vpn-client/Info.plist";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.vpn-client";
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Release;
		};
		0CAE93E4246F4927007B2E95 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_ENTITLEMENTS = "vpn-tunnel/vpn_tunnel.entitlements";
				CODE_SIGN_STYLE = Automatic;
				DEVELOPMENT_TEAM = NR8DLKJ7E6;
				INFOPLIST_FILE = "vpn-tunnel/Info.plist";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
					"@executable_path/../../Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.vpn-client.vpn-tunnel";
				PRODUCT_NAME = "$(TARGET_NAME)";
				SKIP_INSTALL = YES;
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		0CAE93E5246F4927007B2E95 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_ENTITLEMENTS = "vpn-tunnel/vpn_tunnel.entitlements";
				CODE_SIGN_STYLE = Automatic;
				DEVELOPMENT_TEAM = NR8DLKJ7E6;
				INFOPLIST_FILE = "vpn-tunnel/Info.plist";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
					"@executable_path/../../Frameworks",
				);
				PRODUCT_BUNDLE_IDENTIFIER = "com.github.kean.vpn-client.vpn-tunnel";
				PRODUCT_NAME = "$(TARGET_NAME)";
				SKIP_INSTALL = YES;
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Release;
		};
		0CD38AFE2486C12E00DB2626 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_STYLE = Automatic;
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
				OTHER_LDFLAGS = "-ObjC";
				PRODUCT_NAME = BestVPN;
				SKIP_INSTALL = YES;
				SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		0CD38AFF2486C12E00DB2626 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_STYLE = Automatic;
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
				OTHER_LDFLAGS = "-ObjC";
				PRODUCT_NAME = BestVPN;
				SKIP_INSTALL = YES;
				SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		0C189C452479D280000EE6B3 /* Build configuration list for PBXNativeTarget "vpn-server" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				0C189C462479D280000EE6B3 /* Debug */,
				0C189C472479D280000EE6B3 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		0C405BE42466FBFE00EB0786 /* Build configuration list for PBXProject "vpn" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				0C405BFB2466FC0100EB0786 /* Debug */,
				0C405BFC2466FC0100EB0786 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		0C405BFD2466FC0100EB0786 /* Build configuration list for PBXNativeTarget "vpn-client" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				0C405BFE2466FC0100EB0786 /* Debug */,
				0C405BFF2466FC0100EB0786 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		0CAE93E3246F4927007B2E95 /* Build configuration list for PBXNativeTarget "vpn-tunnel" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				0CAE93E4246F4927007B2E95 /* Debug */,
				0CAE93E5246F4927007B2E95 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		0CD38AFD2486C12E00DB2626 /* Build configuration list for PBXNativeTarget "vpn-protocol" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				0CD38AFE2486C12E00DB2626 /* Debug */,
				0CD38AFF2486C12E00DB2626 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
		0CD38B04248735A700DB2626 /* XCRemoteSwiftPackageReference "swift-nio" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/apple/swift-nio";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 2.17.0;
			};
		};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
		0CD38B05248735A700DB2626 /* NIO */ = {
			isa = XCSwiftPackageProductDependency;
			package = 0CD38B04248735A700DB2626 /* XCRemoteSwiftPackageReference "swift-nio" */;
			productName = NIO;
		};
/* End XCSwiftPackageProductDependency section */
	};
	rootObject = 0C405BE12466FBFE00EB0786 /* Project object */;
}


================================================
FILE: vpn.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>


================================================
FILE: vpn.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


================================================
FILE: vpn.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
================================================
{
  "object": {
    "pins": [
      {
        "package": "swift-nio",
        "repositoryURL": "https://github.com/apple/swift-nio",
        "state": {
          "branch": null,
          "revision": "c5fa0b456524cd73dc3ddbb263d4f46c20b86ca3",
          "version": "2.17.0"
        }
      }
    ]
  },
  "version": 1
}


================================================
FILE: vpn.xcodeproj/xcshareddata/xcschemes/vpn-client.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1140"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "0C405BE82466FBFE00EB0786"
               BuildableName = "vpn-client.app"
               BlueprintName = "vpn-client"
               ReferencedContainer = "container:vpn.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "0C405BE82466FBFE00EB0786"
            BuildableName = "vpn-client.app"
            BlueprintName = "vpn-client"
            ReferencedContainer = "container:vpn.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "0C405BE82466FBFE00EB0786"
            BuildableName = "vpn-client.app"
            BlueprintName = "vpn-client"
            ReferencedContainer = "container:vpn.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: vpn.xcodeproj/xcshareddata/xcschemes/vpn-tunnel.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1140"
   wasCreatedForAppExtension = "YES"
   version = "2.0">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "0CAE93D9246F4927007B2E95"
               BuildableName = "vpn-tunnel.appex"
               BlueprintName = "vpn-tunnel"
               ReferencedContainer = "container:vpn.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "0C405BE82466FBFE00EB0786"
               BuildableName = "vpn-client.app"
               BlueprintName = "vpn-client"
               ReferencedContainer = "container:vpn.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = ""
      selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
      launchStyle = "0"
      askForAppToLaunch = "Yes"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES"
      launchAutomaticallySubstyle = "2">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "0C405BE82466FBFE00EB0786"
            BuildableName = "vpn-client.app"
            BlueprintName = "vpn-client"
            ReferencedContainer = "container:vpn.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES"
      launchAutomaticallySubstyle = "2">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "0C405BE82466FBFE00EB0786"
            BuildableName = "vpn-client.app"
            BlueprintName = "vpn-client"
            ReferencedContainer = "container:vpn.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>
Download .txt
gitextract_dp8p67_4/

├── .gitignore
├── README.md
├── vpn-client/
│   ├── AppDelegate.swift
│   ├── Assets.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   └── icon-vpn.imageset/
│   │       └── Contents.json
│   ├── Base.lproj/
│   │   └── LaunchScreen.storyboard
│   ├── Info.plist
│   ├── Preview Content/
│   │   └── Preview Assets.xcassets/
│   │       └── Contents.json
│   ├── SceneDelegate.swift
│   ├── Screens/
│   │   ├── PrimaryButton.swift
│   │   ├── PrimaryButtonView.swift
│   │   ├── RouterView.swift
│   │   ├── Spinner.swift
│   │   ├── SplashView.swift
│   │   ├── TunnelDetails/
│   │   │   ├── TunnelDetailsView.swift
│   │   │   └── TunnelDetailsViewModel.swift
│   │   └── WelcomeView.swift
│   ├── Services/
│   │   ├── Keychain.swift
│   │   └── VPNConfigurationService.swift
│   └── vpn-client.entitlements
├── vpn-protocol/
│   ├── Cipher.swift
│   ├── Packet.swift
│   └── Session.swift
├── vpn-server/
│   └── main.swift
├── vpn-tunnel/
│   ├── Info.plist
│   ├── PacketTunnelProvider.swift
│   └── vpn_tunnel.entitlements
└── vpn.xcodeproj/
    ├── project.pbxproj
    ├── project.xcworkspace/
    │   ├── contents.xcworkspacedata
    │   └── xcshareddata/
    │       ├── IDEWorkspaceChecks.plist
    │       └── swiftpm/
    │           └── Package.resolved
    └── xcshareddata/
        └── xcschemes/
            ├── vpn-client.xcscheme
            └── vpn-tunnel.xcscheme
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (106K chars).
[
  {
    "path": ".gitignore",
    "chars": 968,
    "preview": "## System\n.DS_Store\n\n## Build generated\nbuild/\nDerivedData\nNuke.xcodeproj/xcshareddata/xcbaselines/\n\n## Various settings"
  },
  {
    "path": "README.md",
    "chars": 146,
    "preview": "# VPN (WIP)\n\nA sample VPN client/server written in Swift.\n\n# License\n\nVPN is available under the MIT license. See the LI"
  },
  {
    "path": "vpn-client/AppDelegate.swift",
    "chars": 1365,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport UIKit\n\n@UIApplicationMa"
  },
  {
    "path": "vpn-client/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1591,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "vpn-client/Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "vpn-client/Assets.xcassets/icon-vpn.imageset/Contents.json",
    "chars": 510,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icons8-vpn-status-bar-icon-50.png\",\n      \"idiom\" : \"universal\",\n      \"scale"
  },
  {
    "path": "vpn-client/Base.lproj/LaunchScreen.storyboard",
    "chars": 1665,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
  },
  {
    "path": "vpn-client/Info.plist",
    "chars": 1884,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "vpn-client/Preview Content/Preview Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "vpn-client/SceneDelegate.swift",
    "chars": 2705,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport UIKit\nimport SwiftUI\n\nc"
  },
  {
    "path": "vpn-client/Screens/PrimaryButton.swift",
    "chars": 1312,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport SwiftUI\n\nstruct Primary"
  },
  {
    "path": "vpn-client/Screens/PrimaryButtonView.swift",
    "chars": 1181,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport SwiftUI\n\nstruct Primary"
  },
  {
    "path": "vpn-client/Screens/RouterView.swift",
    "chars": 663,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport SwiftUI\n\nstruct RouterV"
  },
  {
    "path": "vpn-client/Screens/Spinner.swift",
    "chars": 653,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport SwiftUI\nimport UIKit\n\ns"
  },
  {
    "path": "vpn-client/Screens/SplashView.swift",
    "chars": 1553,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport SwiftUI\nimport NetworkE"
  },
  {
    "path": "vpn-client/Screens/TunnelDetails/TunnelDetailsView.swift",
    "chars": 3262,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport SwiftUI\n\nstruct TunnelD"
  },
  {
    "path": "vpn-client/Screens/TunnelDetails/TunnelDetailsViewModel.swift",
    "chars": 5125,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport SwiftUI\nimport Combine\n"
  },
  {
    "path": "vpn-client/Screens/WelcomeView.swift",
    "chars": 2096,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport SwiftUI\n\nstruct Welcome"
  },
  {
    "path": "vpn-client/Services/Keychain.swift",
    "chars": 3265,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport Foundation\n\n/// Wrapper"
  },
  {
    "path": "vpn-client/Services/VPNConfigurationService.swift",
    "chars": 4343,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport Foundation\nimport Combi"
  },
  {
    "path": "vpn-client/vpn-client.entitlements",
    "chars": 430,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "vpn-protocol/Cipher.swift",
    "chars": 1347,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport Foundation\nimport Crypt"
  },
  {
    "path": "vpn-protocol/Packet.swift",
    "chars": 2464,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport Foundation\nimport Crypt"
  },
  {
    "path": "vpn-protocol/Session.swift",
    "chars": 735,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport Foundation\nimport Crypt"
  },
  {
    "path": "vpn-server/main.swift",
    "chars": 4410,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport Foundation\nimport NIO\ni"
  },
  {
    "path": "vpn-tunnel/Info.plist",
    "chars": 1031,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "vpn-tunnel/PacketTunnelProvider.swift",
    "chars": 10722,
    "preview": "// The MIT License (MIT)\n//\n// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).\n\nimport NetworkExtension\nimport"
  },
  {
    "path": "vpn-tunnel/vpn_tunnel.entitlements",
    "chars": 430,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "vpn.xcodeproj/project.pbxproj",
    "chars": 34304,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 52;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "vpn.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 135,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef"
  },
  {
    "path": "vpn.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "vpn.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "chars": 320,
    "preview": "{\n  \"object\": {\n    \"pins\": [\n      {\n        \"package\": \"swift-nio\",\n        \"repositoryURL\": \"https://github.com/apple"
  },
  {
    "path": "vpn.xcodeproj/xcshareddata/xcschemes/vpn-client.xcscheme",
    "chars": 2846,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1140\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "vpn.xcodeproj/xcshareddata/xcschemes/vpn-tunnel.xcscheme",
    "chars": 3536,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1140\"\n   wasCreatedForAppExtension = \"YES\"\n   ve"
  }
]

About this extraction

This page contains the full source code of the kean/VPN GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (95.1 KB), approximately 27.0k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!