js use 1.1.1.1 warp 🙏

This commit is contained in:
Seiike 2025-03-29 08:02:23 +01:00
parent 37808f6af9
commit 1652a0d0a0
2 changed files with 0 additions and 292 deletions

View file

@ -1,280 +0,0 @@
//
// ResolverDNS.swift
// Sulfur
//
// Created by seiike on 28/03/2025.
//
import Foundation
import Network
// MARK: - DNS Provider Enum
enum DNSProvider: String, CaseIterable, Hashable {
case cloudflare = "Cloudflare"
case google = "Google"
case openDNS = "OpenDNS"
case quad9 = "Quad9"
case adGuard = "AdGuard"
case cleanbrowsing = "CleanBrowsing"
case controld = "ControlD"
var servers: [String] {
switch self {
case .cloudflare:
return ["1.1.1.1", "1.0.0.1"]
case .google:
return ["8.8.8.8", "8.8.4.4"]
case .openDNS:
return ["208.67.222.222", "208.67.220.220"]
case .quad9:
return ["9.9.9.9", "149.112.112.112"]
case .adGuard:
return ["94.140.14.14", "94.140.15.15"]
case .cleanbrowsing:
return ["185.228.168.168", "185.228.169.168"]
case .controld:
return ["76.76.2.0", "76.76.10.0"]
}
}
static var current: DNSProvider {
get {
let raw = UserDefaults.standard.string(forKey: "SelectedDNSProvider") ?? DNSProvider.cloudflare.rawValue
return DNSProvider(rawValue: raw) ?? .cloudflare
}
set {
UserDefaults.standard.setValue(newValue.rawValue, forKey: "SelectedDNSProvider")
}
}
}
// MARK: - DNS Resolver Errors
enum DNSResolverError: Error {
case invalidResponse
case noAnswer
case connectionError(String)
case timeout
}
// MARK: - CustomDNSResolver Class
class CustomDNSResolver {
/// Returns an array of DNS servers.
/// If a custom provider ("Custom") is selected in UserDefaults, it returns the custom primary and secondary values;
/// otherwise, it falls back to the default provider's servers.
var dnsServers: [String] {
if let provider = UserDefaults.standard.string(forKey: "CustomDNSProvider"),
provider == "Custom" {
let primary = UserDefaults.standard.string(forKey: "customPrimaryDNS") ?? ""
let secondary = UserDefaults.standard.string(forKey: "customSecondaryDNS") ?? ""
var servers = [String]()
if !primary.isEmpty { servers.append(primary) }
if !secondary.isEmpty { servers.append(secondary) }
if !servers.isEmpty {
return servers
}
}
return DNSProvider.current.servers
}
/// Resolves the provided hostname by sending a DNS query over UDP.
/// - Parameters:
/// - hostname: The hostname to resolve.
/// - timeout: How long to wait for a response (default 5 seconds).
/// - completion: A closure called with the result: a list of IPv4 addresses or an error.
func resolve(hostname: String, timeout: TimeInterval = 5.0, completion: @escaping (Result<[String], Error>) -> Void) {
// Use the first DNS server from our list
guard let dnsServer = dnsServers.first else {
completion(.failure(DNSResolverError.connectionError("No DNS server available")))
return
}
let port: NWEndpoint.Port = 53
let queryID = UInt16.random(in: 0...UInt16.max)
guard let queryData = buildDNSQuery(hostname: hostname, queryID: queryID) else {
completion(.failure(DNSResolverError.connectionError("Failed to build DNS query")))
return
}
// Create a new UDP connection
let connection = NWConnection(host: NWEndpoint.Host(dnsServer), port: port, using: .udp)
// Track connection state manually
var localState = NWConnection.State.setup
connection.stateUpdateHandler = { [weak self] newState in
localState = newState
switch newState {
case .ready:
// Send the DNS query
connection.send(content: queryData, completion: .contentProcessed({ error in
if let error = error {
connection.cancel()
completion(.failure(DNSResolverError.connectionError(error.localizedDescription)))
} else {
// Receive the DNS response
self?.receiveDNSResponse(connection: connection,
expectedQueryID: queryID,
completion: completion)
}
}))
case .failed(let error):
connection.cancel()
completion(.failure(DNSResolverError.connectionError(error.localizedDescription)))
default:
break
}
}
// Start the connection
connection.start(queue: DispatchQueue.global())
// Implement a timeout for the query using a switch on localState
DispatchQueue.global().asyncAfter(deadline: .now() + timeout) {
switch localState {
case .failed(_), .cancelled:
// Already failed or canceled; do nothing
break
default:
// Not failed or canceled => consider it timed out
connection.cancel()
completion(.failure(DNSResolverError.timeout))
}
}
}
// MARK: - Receiving and Parsing
private func receiveDNSResponse(connection: NWConnection,
expectedQueryID: UInt16,
completion: @escaping (Result<[String], Error>) -> Void) {
connection.receiveMessage { [weak self] data, _, _, error in
connection.cancel()
if let error = error {
completion(.failure(DNSResolverError.connectionError(error.localizedDescription)))
return
}
guard let data = data else {
completion(.failure(DNSResolverError.invalidResponse))
return
}
if let ips = self?.parseDNSResponse(data: data, queryID: expectedQueryID), !ips.isEmpty {
completion(.success(ips))
} else {
completion(.failure(DNSResolverError.noAnswer))
}
}
}
// MARK: - DNS Query Construction
/// Constructs a DNS query packet for the given hostname.
/// - Parameters:
/// - hostname: The hostname to resolve.
/// - queryID: A randomly generated query identifier.
/// - Returns: A Data object representing the DNS query.
private func buildDNSQuery(hostname: String, queryID: UInt16) -> Data? {
var data = Data()
// Header: ID (2 bytes)
data.append(contentsOf: withUnsafeBytes(of: queryID.bigEndian, Array.init))
// Flags: standard query with recursion desired (0x0100)
let flags: UInt16 = 0x0100
data.append(contentsOf: withUnsafeBytes(of: flags.bigEndian, Array.init))
// QDCOUNT = 1
let qdcount: UInt16 = 1
data.append(contentsOf: withUnsafeBytes(of: qdcount.bigEndian, Array.init))
// ANCOUNT = 0, NSCOUNT = 0, ARCOUNT = 0
let zero: UInt16 = 0
data.append(contentsOf: withUnsafeBytes(of: zero.bigEndian, Array.init)) // ANCOUNT
data.append(contentsOf: withUnsafeBytes(of: zero.bigEndian, Array.init)) // NSCOUNT
data.append(contentsOf: withUnsafeBytes(of: zero.bigEndian, Array.init)) // ARCOUNT
// Question section:
// QNAME: Encode hostname by splitting into labels.
let labels = hostname.split(separator: ".")
for label in labels {
guard let labelData = label.data(using: .utf8) else {
return nil
}
data.append(UInt8(labelData.count))
data.append(labelData)
}
// Terminate QNAME with zero byte.
data.append(0)
// QTYPE: A record (1)
let qtype: UInt16 = 1
data.append(contentsOf: withUnsafeBytes(of: qtype.bigEndian, Array.init))
// QCLASS: IN (1)
let qclass: UInt16 = 1
data.append(contentsOf: withUnsafeBytes(of: qclass.bigEndian, Array.init))
return data
}
// MARK: - DNS Response Parsing
/// Parses the DNS response packet and extracts IPv4 addresses from A record answers.
/// - Parameters:
/// - data: The DNS response data.
/// - queryID: The expected query identifier.
/// - Returns: An array of IPv4 address strings, or nil if parsing fails.
private func parseDNSResponse(data: Data, queryID: UInt16) -> [String]? {
// Ensure the response is at least long enough for a header.
guard data.count >= 12 else { return nil }
// ID is the first 2 bytes
let responseID = data.subdata(in: 0..<2).withUnsafeBytes { $0.load(as: UInt16.self).bigEndian }
guard responseID == queryID else { return nil }
// ANCOUNT is at offset 6.
let ancount = data.subdata(in: 6..<8).withUnsafeBytes { $0.load(as: UInt16.self).bigEndian }
if ancount == 0 { return nil }
// Skip the header and question section.
var offset = 12
// Skip QNAME
while offset < data.count && data[offset] != 0 {
offset += Int(data[offset]) + 1
}
offset += 1 // Skip the terminating zero.
// Skip QTYPE (2 bytes) and QCLASS (2 bytes)
offset += 4
var ips: [String] = []
// Loop through answer records.
for _ in 0..<ancount {
if offset + 12 > data.count { break }
offset += 2 // Skip NAME (pointer)
let type = data.subdata(in: offset..<(offset+2)).withUnsafeBytes { $0.load(as: UInt16.self).bigEndian }
offset += 2 // TYPE
offset += 2 // CLASS
offset += 4 // TTL
let rdlength = data.subdata(in: offset..<(offset+2)).withUnsafeBytes { $0.load(as: UInt16.self).bigEndian }
offset += 2
// If the record is an A record and the length is 4 bytes, extract the IPv4 address.
if type == 1 && rdlength == 4 && offset + 4 <= data.count {
let ipBytes = data.subdata(in: offset..<(offset+4))
let ip = ipBytes.map { String($0) }.joined(separator: ".")
ips.append(ip)
}
offset += Int(rdlength)
}
return ips
}
}

View file

@ -59,7 +59,6 @@
13EA2BD52D32D97400C1EBD7 /* CustomPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */; };
13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */; };
13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */; };
1E2719D92D975126008C4BD0 /* ResolverDNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2719D82D975120008C4BD0 /* ResolverDNS.swift */; };
1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */; };
1EAC7A322D888BC50083984D /* MusicProgressSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EAC7A312D888BC50083984D /* MusicProgressSlider.swift */; };
73D164D52D8B5B470011A360 /* JavaScriptCore+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73D164D42D8B5B340011A360 /* JavaScriptCore+Extensions.swift */; };
@ -118,7 +117,6 @@
13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomPlayer.swift; sourceTree = "<group>"; };
13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = "<group>"; };
13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NormalPlayer.swift; sourceTree = "<group>"; };
1E2719D82D975120008C4BD0 /* ResolverDNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResolverDNS.swift; sourceTree = "<group>"; };
1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewLoggerFilter.swift; sourceTree = "<group>"; };
1EAC7A312D888BC50083984D /* MusicProgressSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicProgressSlider.swift; sourceTree = "<group>"; };
73D164D42D8B5B340011A360 /* JavaScriptCore+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JavaScriptCore+Extensions.swift"; sourceTree = "<group>"; };
@ -286,7 +284,6 @@
133D7C852D2BE2640075467E /* Utils */ = {
isa = PBXGroup;
children = (
1E2719D72D9750FB008C4BD0 /* Networking */,
13DB7CEA2D7DED50004371D3 /* DownloadManager */,
13C0E5E82D5F85DD00E7F619 /* ContinueWatching */,
13103E8C2D58E037000F0673 /* SkeletonCells */,
@ -440,14 +437,6 @@
path = Components;
sourceTree = "<group>";
};
1E2719D72D9750FB008C4BD0 /* Networking */ = {
isa = PBXGroup;
children = (
1E2719D82D975120008C4BD0 /* ResolverDNS.swift */,
);
path = Networking;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -535,7 +524,6 @@
13B7F4C12D58FFDD0045714A /* Shimmer.swift in Sources */,
139935662D468C450065CEFF /* ModuleManager.swift in Sources */,
133D7C902D2BE2640075467E /* SettingsView.swift in Sources */,
1E2719D92D975126008C4BD0 /* ResolverDNS.swift in Sources */,
1334FF4F2D786C9E007E289F /* TMDB-Trending.swift in Sources */,
13CBEFDA2D5F7D1200D011EE /* String.swift in Sources */,
13DB46902D900A38008CBC03 /* URL.swift in Sources */,