Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 52c59704de | |||
| 0b828f9b96 |
@@ -1,2 +1,2 @@
|
||||
VERSION_NAME = 0.0.20181225
|
||||
VERSION_ID = 1
|
||||
VERSION_ID = 2
|
||||
|
||||
@@ -45,7 +45,6 @@ class DNSResolver {
|
||||
let resolvedEndpoint = tuple.1
|
||||
if let endpoint = endpoint {
|
||||
if resolvedEndpoint == nil {
|
||||
// DNS resolution failed
|
||||
guard let hostname = endpoint.hostname() else { fatalError() }
|
||||
hostnamesWithDnsResolutionFailure.append(hostname)
|
||||
}
|
||||
@@ -57,81 +56,97 @@ class DNSResolver {
|
||||
}
|
||||
return resolvedEndpoints
|
||||
}
|
||||
}
|
||||
|
||||
extension DNSResolver {
|
||||
// Based on DNS resolution code by Jason Donenfeld <jason@zx2c4.com>
|
||||
// in parse_endpoint() in src/tools/config.c in the WireGuard codebase
|
||||
|
||||
//swiftlint:disable:next cyclomatic_complexity
|
||||
private static func resolveSync(endpoint: Endpoint) -> Endpoint? {
|
||||
switch endpoint.host {
|
||||
case .name(let name, _):
|
||||
var resultPointer = UnsafeMutablePointer<addrinfo>(OpaquePointer(bitPattern: 0))
|
||||
var hints = addrinfo(
|
||||
ai_flags: AI_ALL, // We set this to ALL so that we get v4 addresses even on DNS64 networks
|
||||
ai_family: AF_UNSPEC,
|
||||
ai_socktype: SOCK_DGRAM,
|
||||
ai_protocol: IPPROTO_UDP,
|
||||
ai_addrlen: 0,
|
||||
ai_canonname: nil,
|
||||
ai_addr: nil,
|
||||
ai_next: nil)
|
||||
if getaddrinfo(name, "\(endpoint.port)", &hints, &resultPointer) != 0 {
|
||||
return nil
|
||||
}
|
||||
var next = resultPointer
|
||||
var ipv4Address: IPv4Address?
|
||||
var ipv6Address: IPv6Address?
|
||||
while next != nil {
|
||||
let result = next!.pointee
|
||||
next = result.ai_next
|
||||
if result.ai_family == AF_INET && result.ai_addrlen == MemoryLayout<sockaddr_in>.size {
|
||||
var sa4 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in.self).pointee
|
||||
ipv4Address = IPv4Address(Data(bytes: &sa4.sin_addr, count: MemoryLayout<in_addr>.size))
|
||||
break // If we found an IPv4 address, we can stop
|
||||
} else if result.ai_family == AF_INET6 && result.ai_addrlen == MemoryLayout<sockaddr_in6>.size {
|
||||
var sa6 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in6.self).pointee
|
||||
ipv6Address = IPv6Address(Data(bytes: &sa6.sin6_addr, count: MemoryLayout<in6_addr>.size))
|
||||
continue // If we already have an IPv6 address, we can skip this one
|
||||
}
|
||||
}
|
||||
freeaddrinfo(resultPointer)
|
||||
|
||||
// The endpoint is a hostname and needs DNS resolution
|
||||
if addressInfo(for: name, port: endpoint.port, resultPointer: &resultPointer) == 0 {
|
||||
// getaddrinfo succeeded
|
||||
let ipv4Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET_ADDRSTRLEN))
|
||||
let ipv6Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN))
|
||||
var ipv4AddressString: String?
|
||||
var ipv6AddressString: String?
|
||||
while resultPointer != nil {
|
||||
let result = resultPointer!.pointee
|
||||
resultPointer = result.ai_next
|
||||
if result.ai_family == AF_INET && result.ai_addrlen == MemoryLayout<sockaddr_in>.size {
|
||||
var sa4 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in.self).pointee
|
||||
if inet_ntop(result.ai_family, &sa4.sin_addr, ipv4Buffer, socklen_t(INET_ADDRSTRLEN)) != nil {
|
||||
ipv4AddressString = String(cString: ipv4Buffer)
|
||||
// If we found an IPv4 address, we can stop
|
||||
break
|
||||
}
|
||||
} else if result.ai_family == AF_INET6 && result.ai_addrlen == MemoryLayout<sockaddr_in6>.size {
|
||||
if ipv6AddressString != nil {
|
||||
// If we already have an IPv6 address, we can skip this one
|
||||
continue
|
||||
}
|
||||
var sa6 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in6.self).pointee
|
||||
if inet_ntop(result.ai_family, &sa6.sin6_addr, ipv6Buffer, socklen_t(INET6_ADDRSTRLEN)) != nil {
|
||||
ipv6AddressString = String(cString: ipv6Buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
ipv4Buffer.deallocate()
|
||||
ipv6Buffer.deallocate()
|
||||
// We prefer an IPv4 address over an IPv6 address
|
||||
if let ipv4AddressString = ipv4AddressString, let ipv4Address = IPv4Address(ipv4AddressString) {
|
||||
return Endpoint(host: .ipv4(ipv4Address), port: endpoint.port)
|
||||
} else if let ipv6AddressString = ipv6AddressString, let ipv6Address = IPv6Address(ipv6AddressString) {
|
||||
return Endpoint(host: .ipv6(ipv6Address), port: endpoint.port)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
// We prefer an IPv4 address over an IPv6 address
|
||||
if let ipv4Address = ipv4Address {
|
||||
return Endpoint(host: .ipv4(ipv4Address), port: endpoint.port)
|
||||
} else if let ipv6Address = ipv6Address {
|
||||
return Endpoint(host: .ipv6(ipv6Address), port: endpoint.port)
|
||||
} else {
|
||||
// getaddrinfo failed
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
// The endpoint is already resolved
|
||||
return endpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func addressInfo(for name: String, port: NWEndpoint.Port, resultPointer: inout UnsafeMutablePointer<addrinfo>?) -> Int32 {
|
||||
extension Endpoint {
|
||||
func withReresolvedIP() -> Endpoint {
|
||||
var ret = self
|
||||
let hostname: String
|
||||
switch host {
|
||||
case .name(let name, _):
|
||||
hostname = name
|
||||
case .ipv4(let address):
|
||||
hostname = "\(address)"
|
||||
case .ipv6(let address):
|
||||
hostname = "\(address)"
|
||||
}
|
||||
|
||||
var resultPointer = UnsafeMutablePointer<addrinfo>(OpaquePointer(bitPattern: 0))
|
||||
var hints = addrinfo(
|
||||
ai_flags: 0,
|
||||
ai_flags: 0, // We set this to zero so that we actually resolve this using DNS64
|
||||
ai_family: AF_UNSPEC,
|
||||
ai_socktype: SOCK_DGRAM, // WireGuard is UDP-only
|
||||
ai_protocol: IPPROTO_UDP, // WireGuard is UDP-only
|
||||
ai_socktype: SOCK_DGRAM,
|
||||
ai_protocol: IPPROTO_UDP,
|
||||
ai_addrlen: 0,
|
||||
ai_canonname: nil,
|
||||
ai_addr: nil,
|
||||
ai_next: nil)
|
||||
|
||||
return getaddrinfo(
|
||||
name.cString(using: .utf8), // Hostname
|
||||
"\(port)".cString(using: .utf8), // Port
|
||||
&hints,
|
||||
&resultPointer)
|
||||
if getaddrinfo(hostname, "\(port)", &hints, &resultPointer) != 0 || resultPointer == nil {
|
||||
return ret
|
||||
}
|
||||
let result = resultPointer!.pointee
|
||||
if result.ai_family == AF_INET && result.ai_addrlen == MemoryLayout<sockaddr_in>.size {
|
||||
var sa4 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in.self).pointee
|
||||
let addr = IPv4Address(Data(bytes: &sa4.sin_addr, count: MemoryLayout<in_addr>.size))
|
||||
ret = Endpoint(host: .ipv4(addr!), port: port)
|
||||
} else if result.ai_family == AF_INET6 && result.ai_addrlen == MemoryLayout<sockaddr_in6>.size {
|
||||
var sa6 = UnsafeRawPointer(result.ai_addr)!.assumingMemoryBound(to: sockaddr_in6.self).pointee
|
||||
let addr = IPv6Address(Data(bytes: &sa6.sin6_addr, count: MemoryLayout<in6_addr>.size))
|
||||
ret = Endpoint(host: .ipv6(addr!), port: port)
|
||||
}
|
||||
freeaddrinfo(resultPointer)
|
||||
if ret.host != host {
|
||||
wg_log(.debug, message: "DNS64: mapped \(host) to \(ret.host)")
|
||||
} else {
|
||||
wg_log(.debug, message: "DNS64: mapped \(host) to itself.")
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
networkMonitor?.cancel()
|
||||
}
|
||||
|
||||
//swiftlint:disable:next function_body_length
|
||||
override func startTunnel(options: [String: NSObject]?, completionHandler startTunnelCompletionHandler: @escaping (Error?) -> Void) {
|
||||
let activationAttemptId = options?["activationAttemptId"] as? String
|
||||
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
|
||||
@@ -65,6 +66,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
if getsockopt(fileDescriptor, 2 /* SYSPROTO_CONTROL */, 2 /* UTUN_OPT_IFNAME */, ifnamePtr, &ifnameSize) == 0 {
|
||||
self.ifname = String(cString: ifnamePtr)
|
||||
}
|
||||
ifnamePtr.deallocate()
|
||||
wg_log(.info, message: "Tunnel interface is \(self.ifname ?? "unknown")")
|
||||
let handle = self.packetTunnelSettingsGenerator!.uapiConfiguration().withGoString { return wgTurnOn($0, fileDescriptor) }
|
||||
if handle < 0 {
|
||||
|
||||
@@ -18,7 +18,7 @@ class PacketTunnelSettingsGenerator {
|
||||
var wgSettings = ""
|
||||
for (index, peer) in tunnelConfiguration.peers.enumerated() {
|
||||
wgSettings.append("public_key=\(peer.publicKey.hexEncodedString())\n")
|
||||
if let endpoint = resolvedEndpoints[index] {
|
||||
if let endpoint = resolvedEndpoints[index]?.withReresolvedIP() {
|
||||
if case .name(_, _) = endpoint.host { assert(false, "Endpoint is not resolved") }
|
||||
wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class PacketTunnelSettingsGenerator {
|
||||
if let preSharedKey = peer.preSharedKey {
|
||||
wgSettings.append("preshared_key=\(preSharedKey.hexEncodedString())\n")
|
||||
}
|
||||
if let endpoint = resolvedEndpoints[index] {
|
||||
if let endpoint = resolvedEndpoints[index]?.withReresolvedIP() {
|
||||
if case .name(_, _) = endpoint.host { assert(false, "Endpoint is not resolved") }
|
||||
wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
|
||||
}
|
||||
@@ -63,18 +63,7 @@ class PacketTunnelSettingsGenerator {
|
||||
* make sense. So, we fill it in with this placeholder, which is not
|
||||
* a valid IP address that will actually route over the Internet.
|
||||
*/
|
||||
var remoteAddress = "0.0.0.0"
|
||||
let endpointsCompact = resolvedEndpoints.compactMap { $0 }
|
||||
if endpointsCompact.count == 1 {
|
||||
switch endpointsCompact.first!.host {
|
||||
case .ipv4(let address):
|
||||
remoteAddress = "\(address)"
|
||||
case .ipv6(let address):
|
||||
remoteAddress = "\(address)"
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
let remoteAddress = "0.0.0.0"
|
||||
|
||||
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: remoteAddress)
|
||||
|
||||
@@ -93,16 +82,13 @@ class PacketTunnelSettingsGenerator {
|
||||
|
||||
let (ipv4Routes, ipv6Routes) = routes()
|
||||
let (ipv4IncludedRoutes, ipv6IncludedRoutes) = includedRoutes()
|
||||
let (ipv4ExcludedRoutes, ipv6ExcludedRoutes) = excludedRoutes()
|
||||
|
||||
let ipv4Settings = NEIPv4Settings(addresses: ipv4Routes.map { $0.destinationAddress }, subnetMasks: ipv4Routes.map { $0.destinationSubnetMask })
|
||||
ipv4Settings.includedRoutes = ipv4IncludedRoutes
|
||||
ipv4Settings.excludedRoutes = ipv4ExcludedRoutes
|
||||
networkSettings.ipv4Settings = ipv4Settings
|
||||
|
||||
let ipv6Settings = NEIPv6Settings(addresses: ipv6Routes.map { $0.destinationAddress }, networkPrefixLengths: ipv6Routes.map { $0.destinationNetworkPrefixLength })
|
||||
ipv6Settings.includedRoutes = ipv6IncludedRoutes
|
||||
ipv6Settings.excludedRoutes = ipv6ExcludedRoutes
|
||||
networkSettings.ipv6Settings = ipv6Settings
|
||||
|
||||
return networkSettings
|
||||
@@ -152,24 +138,6 @@ class PacketTunnelSettingsGenerator {
|
||||
}
|
||||
return (ipv4IncludedRoutes, ipv6IncludedRoutes)
|
||||
}
|
||||
|
||||
private func excludedRoutes() -> ([NEIPv4Route], [NEIPv6Route]) {
|
||||
var ipv4ExcludedRoutes = [NEIPv4Route]()
|
||||
var ipv6ExcludedRoutes = [NEIPv6Route]()
|
||||
for endpoint in resolvedEndpoints {
|
||||
guard let endpoint = endpoint else { continue }
|
||||
switch endpoint.host {
|
||||
case .ipv4(let address):
|
||||
ipv4ExcludedRoutes.append(NEIPv4Route(destinationAddress: "\(address)", subnetMask: "255.255.255.255"))
|
||||
case .ipv6(let address):
|
||||
ipv6ExcludedRoutes.append(NEIPv6Route(destinationAddress: "\(address)", networkPrefixLength: NSNumber(value: UInt8(128))))
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
return (ipv4ExcludedRoutes, ipv6ExcludedRoutes)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension Data {
|
||||
|
||||
Reference in New Issue
Block a user