]> git.ipfire.org Git - thirdparty/wireguard-apple.git/commitdiff
VPN: DNSResolver: A class that does DNS resolution using getaddrinfo
authorRoopesh Chander <roop@roopc.net>
Fri, 26 Oct 2018 12:52:06 +0000 (18:22 +0530)
committerRoopesh Chander <roop@roopc.net>
Sat, 27 Oct 2018 13:37:16 +0000 (19:07 +0530)
Signed-off-by: Roopesh Chander <roop@roopc.net>
WireGuard/WireGuard.xcodeproj/project.pbxproj
WireGuard/WireGuard/VPN/DNSResolver.swift [new file with mode: 0644]

index 741e4191228844ad32fb26f745e8e801d3a2f47a..e8feb5c70e7c9134fc2686b872c57c449f1fa101 100644 (file)
@@ -7,6 +7,7 @@
        objects = {
 
 /* Begin PBXBuildFile section */
+               6F5D0C1521832391000F85AD /* DNSResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5D0C1421832391000F85AD /* DNSResolver.swift */; };
                6F628C3D217F09E9003482A3 /* TunnelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */; };
                6F628C3F217F3413003482A3 /* DNSServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3E217F3413003482A3 /* DNSServer.swift */; };
                6F628C41217F47DB003482A3 /* TunnelDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C40217F47DB003482A3 /* TunnelDetailTableViewController.swift */; };
@@ -28,6 +29,7 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+               6F5D0C1421832391000F85AD /* DNSResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSResolver.swift; sourceTree = "<group>"; };
                6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelViewModel.swift; sourceTree = "<group>"; };
                6F628C3E217F3413003482A3 /* DNSServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSServer.swift; sourceTree = "<group>"; };
                6F628C40217F47DB003482A3 /* TunnelDetailTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelDetailTableViewController.swift; sourceTree = "<group>"; };
                        isa = PBXGroup;
                        children = (
                                6F7774EE21722D97006A79B3 /* TunnelsManager.swift */,
+                               6F5D0C1421832391000F85AD /* DNSResolver.swift */,
                        );
                        path = VPN;
                        sourceTree = "<group>";
                                6F6899AC218099F00012E523 /* WgQuickConfigFileParser.swift in Sources */,
                                6F7774E421718281006A79B3 /* TunnelsListTableViewController.swift in Sources */,
                                6F7774EF21722D97006A79B3 /* TunnelsManager.swift in Sources */,
+                               6F5D0C1521832391000F85AD /* DNSResolver.swift in Sources */,
                                6F693A562179E556008551C1 /* Endpoint.swift in Sources */,
                                6F6899A62180447E0012E523 /* x25519.c in Sources */,
                                6F7774E2217181B1006A79B3 /* AppDelegate.swift in Sources */,
diff --git a/WireGuard/WireGuard/VPN/DNSResolver.swift b/WireGuard/WireGuard/VPN/DNSResolver.swift
new file mode 100644 (file)
index 0000000..89b1a83
--- /dev/null
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All rights reserved.
+
+import Network
+import Foundation
+
+class DNSResolver {
+    let endpoints: [Endpoint]
+
+    init(endpoints: [Endpoint]) {
+        self.endpoints = endpoints
+    }
+
+    func resolve(completionHandler: @escaping ([Endpoint?]) -> Void) {
+        let endpoints = self.endpoints
+        DispatchQueue.global(qos: .userInitiated).async {
+            var resolvedEndpoints = Array<Endpoint?>(repeating: nil, count: endpoints.count)
+            for (i, endpoint) in endpoints.enumerated() {
+                let resolvedEndpoint = DNSResolver.resolveSync(endpoint: endpoint)
+                resolvedEndpoints[i] = resolvedEndpoint
+            }
+            DispatchQueue.main.async {
+                completionHandler(resolvedEndpoints)
+            }
+        }
+    }
+
+    // Based on DNS resolution code by Jason Donenfeld <jason@zx2c4.com>
+    // in parse_endpoint() in src/tools/config.c in the WireGuard codebase
+    private static func resolveSync(endpoint: Endpoint) -> Endpoint? {
+        var hints = addrinfo(
+            ai_flags: 0,
+            ai_family: AF_UNSPEC,
+            ai_socktype: SOCK_DGRAM, // WireGuard is UDP-only
+            ai_protocol: IPPROTO_UDP, // WireGuard is UDP-only
+            ai_addrlen: 0,
+            ai_canonname: nil,
+            ai_addr: nil,
+            ai_next: nil)
+        var resultPointer = UnsafeMutablePointer<addrinfo>(OpaquePointer(bitPattern: 0))
+        switch (endpoint.host) {
+        case .name(let name, _):
+            // The endpoint is a hostname and needs DNS resolution
+            let returnValue = getaddrinfo(
+                name.cString(using: .utf8), // Hostname
+                "\(endpoint.port)".cString(using: .utf8), // Port
+                &hints,
+                &resultPointer)
+            if (returnValue == 0) {
+                // getaddrinfo succeeded
+                let ipv4Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET_ADDRSTRLEN))
+                let ipv6Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN))
+                var ipv4AddressString: String? = nil
+                var ipv6AddressString: String? = nil
+                while (resultPointer != nil) {
+                    let result = resultPointer!.pointee
+                    resultPointer = result.ai_next
+                    if (result.ai_family == AF_INET && result.ai_addrlen == INET_ADDRSTRLEN) {
+                        if (inet_ntop(result.ai_family, result.ai_addr, ipv4Buffer, result.ai_addrlen) != 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 == INET6_ADDRSTRLEN) {
+                        if (ipv6AddressString != nil) {
+                            // If we already have an IPv6 address, we can skip this one
+                            continue
+                        }
+                        if (inet_ntop(result.ai_family, result.ai_addr, ipv6Buffer, result.ai_addrlen) != 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: NWEndpoint.Host.ipv4(ipv4Address), port: endpoint.port)
+                } else if let ipv6AddressString = ipv6AddressString, let ipv6Address = IPv6Address(ipv6AddressString) {
+                    return Endpoint(host: NWEndpoint.Host.ipv6(ipv6Address), port: endpoint.port)
+                } else {
+                    return nil
+                }
+            } else {
+                // getaddrinfo failed
+                return nil
+            }
+        default:
+            // The endpoint is already resolved
+            return endpoint
+        }
+    }
+}