summaryrefslogtreecommitdiffhomepage
path: root/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift
blob: 2383142ba104c6e3867bafde4ebe6954db0e1d4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//
//  BlockedStateErrorMapper.swift
//  PacketTunnel
//
//  Created by pronebird on 14/09/2023.
//  Copyright © 2025 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadREST
import MullvadSettings
import MullvadTypes
import PacketTunnelCore
import WireGuardKit

/**
 Struct responsible for mapping errors that may occur in the packet tunnel to the `BlockedStateReason`.
 */
public struct BlockedStateErrorMapper: BlockedStateErrorMapperProtocol {
    public func mapError(_ error: Error) -> BlockedStateReason {
        switch error {
        case let error as ReadDeviceDataError:
            // Such error is thrown by implementations of `SettingsReaderProtocol`.
            switch error {
            case .loggedOut:
                return .deviceLoggedOut
            case .revoked:
                return .deviceRevoked
            }

        case is UnsupportedSettingsVersionError:
            // Can be returned after updating the app. The tunnel is usually restarted right after but the main app
            // needs to be launched to perform settings migration.
            return .outdatedSchema

        case let keychainError as KeychainError where keychainError == .interactionNotAllowed:
            // Returned when reading device state from Keychain when it is locked on device boot.
            return .deviceLocked

        case let error as ReadSettingsVersionError:
            // Returned when reading tunnel settings from Keychain.
            // interactionNotAllowed is returned when device is locked on boot, otherwise it must be a generic error
            // when reading settings from keychain.
            if case KeychainError.interactionNotAllowed = error.underlyingError as? KeychainError {
                return .deviceLocked
            } else {
                return .readSettings
            }

        case let error as NoRelaysSatisfyingConstraintsError:
            // Returned by relay selector when there are no relays satisfying the given constraints.
            return switch error.reason {
            case .filterConstraintNotMatching:
                .noRelaysSatisfyingFilterConstraints
            case .entryEqualsExit:
                .multihopEntryEqualsExit
            case .noDaitaRelaysFound:
                .noRelaysSatisfyingDaitaConstraints
            case .noObfuscatedRelaysFound:
                .noRelaysSatisfyingObfuscationSettings
            case .invalidPort:
                .noRelaysSatisfyingPortConstraints
            default:
                .noRelaysSatisfyingConstraints
            }

        case is WireGuardAdapterError:
            // Any errors that originate from wireguard adapter including failure to set tunnel settings using
            // packet tunnel provider.
            return .tunnelAdapter

        case is PublicKeyError:
            // Returned when there is an endpoint but its public key is invalid.
            return .invalidRelayPublicKey

        default:
            // Everything else in case we introduce new errors and forget to handle them.
            return .unknown
        }
    }
}