summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadSettings/DNSSettings.swift
blob: a696be398674cd50da7eada861274928bc33b3ff (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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//
//  DNSSettings.swift
//  MullvadVPN
//
//  Created by pronebird on 27/04/2022.
//  Copyright © 2025 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadTypes
import Network

/// A struct describing Mullvad DNS blocking options.
public struct DNSBlockingOptions: OptionSet, Codable, Sendable {
    public let rawValue: UInt32

    public static let blockAdvertising = DNSBlockingOptions(rawValue: 1 << 0)
    public static let blockTracking = DNSBlockingOptions(rawValue: 1 << 1)
    public static let blockMalware = DNSBlockingOptions(rawValue: 1 << 2)
    public static let blockAdultContent = DNSBlockingOptions(rawValue: 1 << 3)
    public static let blockGambling = DNSBlockingOptions(rawValue: 1 << 4)
    public static let blockSocialMedia = DNSBlockingOptions(rawValue: 1 << 5)

    public var serverAddress: IPv4Address? {
        if isEmpty {
            return nil
        } else {
            return IPv4Address("100.64.0.\(rawValue)")
        }
    }

    public init(rawValue: UInt32) {
        self.rawValue = rawValue
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let rawValue = try container.decode(RawValue.self)

        self.init(rawValue: rawValue)
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()

        try container.encode(rawValue)
    }
}

/// A struct that holds DNS settings.
public struct DNSSettings: Codable, Equatable, Sendable {
    /// Maximum number of allowed DNS domains.
    public static let maxAllowedCustomDNSDomains = 3

    /// DNS blocking options.
    public var blockingOptions: DNSBlockingOptions = []

    /// Enable custom DNS.
    public var enableCustomDNS = false

    /// Custom DNS domains.
    public var customDNSDomains: [AnyIPAddress] = []

    /// Effective state of the custom DNS setting.
    public var effectiveEnableCustomDNS: Bool {
        blockingOptions.isEmpty && enableCustomDNS && !customDNSDomains.isEmpty
    }

    private enum CodingKeys: String, CodingKey {
        // Removed in 2022.1 in favor of `blockingOptions`
        case blockAdvertising, blockTracking

        // Added in 2022.1
        case blockingOptions

        case enableCustomDNS, customDNSDomains
    }

    public init() {}

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        // Added in 2022.1
        if let storedBlockingOptions = try container.decodeIfPresent(
            DNSBlockingOptions.self,
            forKey: .blockingOptions
        ) {
            blockingOptions = storedBlockingOptions
        }

        if let storedBlockAdvertising = try container.decodeIfPresent(
            Bool.self,
            forKey: .blockAdvertising
        ), storedBlockAdvertising {
            blockingOptions.insert(.blockAdvertising)
        }

        if let storedBlockTracking = try container.decodeIfPresent(
            Bool.self,
            forKey: .blockTracking
        ), storedBlockTracking {
            blockingOptions.insert(.blockTracking)
        }

        if let storedEnableCustomDNS = try container.decodeIfPresent(
            Bool.self,
            forKey: .enableCustomDNS
        ) {
            enableCustomDNS = storedEnableCustomDNS
        }

        if let storedCustomDNSDomains = try container.decodeIfPresent(
            [AnyIPAddress].self,
            forKey: .customDNSDomains
        ) {
            customDNSDomains = storedCustomDNSDomains
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(blockingOptions, forKey: .blockingOptions)
        try container.encode(enableCustomDNS, forKey: .enableCustomDNS)
        try container.encode(customDNSDomains, forKey: .customDNSDomains)
    }
}