-
Notifications
You must be signed in to change notification settings - Fork 342
Expand file tree
/
Copy pathCIDR.swift
More file actions
148 lines (134 loc) · 5.11 KB
/
Copy pathCIDR.swift
File metadata and controls
148 lines (134 loc) · 5.11 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// Describes an IPv4 or IPv6 CIDR address block.
@frozen
public enum CIDR: CustomStringConvertible, Equatable, Sendable, Hashable {
case v4(IPv4Address, Prefix)
case v6(IPv6Address, Prefix)
/// Create a CIDR address block.
public init(_ cidr: String) throws {
if let cidrV4 = try? CIDRv4(cidr) {
self = .v4(cidrV4.address, cidrV4.prefix)
} else if let cidrV6 = try? CIDRv6(cidr) {
self = .v6(cidrV6.address, cidrV6.prefix)
} else {
throw Error.invalidCIDR(cidr: cidr)
}
}
/// Create a CIDR address from a member IP and a prefix length.
public init(_ address: IPAddress, prefix: Prefix) throws {
switch address {
case .v4(let addr):
guard prefix.length <= 32 else {
throw Error.invalidCIDR(cidr: "\(address)/\(prefix)")
}
self = .v4(addr, prefix)
case .v6(let addr):
guard prefix.length <= 128 else {
throw Error.invalidCIDR(cidr: "\(address)/\(prefix)")
}
self = .v6(addr, prefix)
}
}
/// Create the smallest CIDR block that includes the lower and upper bounds.
///
/// For type-safe construction, prefer `v4Range(lower:upper:)` or `v6Range(lower:upper:)`.
public init(lower: IPAddress, upper: IPAddress) throws {
switch (lower, upper) {
case (.v4(let lowerAddr), .v4(let upperAddr)):
let cidr = try CIDRv4(lower: lowerAddr, upper: upperAddr)
self = .v4(cidr.address, cidr.prefix)
case (.v6(let lowerAddr), .v6(let upperAddr)):
let cidr = try CIDRv6(lower: lowerAddr, upper: upperAddr)
self = .v6(cidr.address, cidr.prefix)
default:
throw Error.invalidAddressRange(lower: lower.description, upper: upper.description)
}
}
/// The IP component of this CIDR address.
@inlinable
public var address: IPAddress {
switch self {
case .v4(let addr, _):
return .v4(addr)
case .v6(let addr, _):
return .v6(addr)
}
}
/// The prefix length of this CIDR address.
@inlinable
public var prefix: Prefix {
switch self {
case .v4(_, let prefix), .v6(_, let prefix):
return prefix
}
}
/// The lowest address in this CIDR block
@inlinable
public var lower: IPAddress {
switch self {
case (.v4(let addr, let prefix)):
return .v4(IPv4Address(addr.value & prefix.prefixMask32))
case (.v6(let addr, let prefix)):
return .v6(IPv6Address(addr.value & prefix.prefixMask128, zone: addr.zone))
}
}
/// The highest address in this CIDR block (broadcast address).
@inlinable
public var upper: IPAddress {
switch self {
case .v4(let addr, let prefix):
return .v4(IPv4Address(addr.value | prefix.suffixMask32))
case .v6(let addr, let prefix):
return .v6(IPv6Address(addr.value | prefix.suffixMask128, zone: addr.zone))
}
}
/// Return true if the CIDR block contains the specified address.
///
/// Compares network portion of the given IP address.
@inlinable
public func contains(_ ip: IPAddress) -> Bool {
switch (self, ip) {
case (.v4(let network, let prefix), .v4(let ip)):
return (network.value & prefix.prefixMask32) == (ip.value & prefix.prefixMask32)
case (.v6(let network, let prefix), .v6(let ip)):
return (network.zone == ip.zone) && ((network.value & prefix.prefixMask128) == (ip.value & prefix.prefixMask128))
default:
return false
}
}
/// Retrieve the text representation of the CIDR block.
public var description: String {
"\(address)/\(prefix)"
}
}
extension CIDR {
public enum Error: Swift.Error {
case invalidCIDR(cidr: String)
case invalidAddressRange(lower: String, upper: String)
}
}
extension CIDR: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
try self.init(string)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(description)
}
}