blob: c11936e4536dcb534561986e166920c357836747 (
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
|
//
// Socks5Endpoint.swift
// MullvadTransport
//
// Created by pronebird on 20/10/2023.
//
import Foundation
import MullvadTypes
import Network
/// A network endpoint specified by DNS name and port.
public struct Socks5HostEndpoint: Sendable {
/// The endpoint's hostname.
public let hostname: String
/// The endpoint's port.
public let port: UInt16
/**
Initializes a new host endpoint.
Returns `nil` when the hostname is either empty or longer than 255 bytes, because it cannot be represented in socks protocol.
- Parameters:
- hostname: the endpoint's hostname
- port: the endpoint's port
*/
public init?(hostname: String, port: UInt16) {
// The maximum length of domain name in bytes.
let maxHostnameLength = UInt8.max
let hostnameByteLength = Data(hostname.utf8).count
// Empty hostname is meaningless.
guard hostnameByteLength > 0 else { return nil }
// The length larger than 255 bytes cannot be represented in socks protocol.
guard hostnameByteLength <= maxHostnameLength else { return nil }
self.hostname = hostname
self.port = port
}
}
/// The endpoint type used by objects implementing socks protocol.
public enum Socks5Endpoint: Sendable {
/// IPv4 endpoint.
case ipv4(IPv4Endpoint)
/// IPv6 endpoint.
case ipv6(IPv6Endpoint)
/// Domain name endpoint.
case domain(Socks5HostEndpoint)
/// The corresponding raw socks address type.
var addressType: Socks5AddressType {
switch self {
case .ipv4:
return .ipv4
case .ipv6:
return .ipv6
case .domain:
return .domainName
}
}
/// The port associated with the underlying endpoint.
var port: UInt16 {
switch self {
case let .ipv4(endpoint):
endpoint.port
case let .ipv6(endpoint):
endpoint.port
case let .domain(endpoint):
endpoint.port
}
}
/// The byte representation in socks protocol.
var rawData: Data {
var data = Data()
switch self {
case let .ipv4(endpoint):
data.append(contentsOf: endpoint.ip.rawValue)
case let .ipv6(endpoint):
data.append(contentsOf: endpoint.ip.rawValue)
case let .domain(endpoint):
// Convert hostname to byte data without nul terminator.
let domainNameBytes = Data(endpoint.hostname.utf8)
// Append the length of domain name.
// Host endpoint already ensures that the length of domain name does not exceed the maximum value that
// single byte can hold.
data.append(UInt8(domainNameBytes.count))
// Append the domain name.
data.append(contentsOf: domainNameBytes)
}
// Append port in network byte order.
withUnsafeBytes(of: port.bigEndian) { buffer in
data.append(contentsOf: buffer)
}
return data
}
}
|