184 lines
5.7 KiB
Swift
184 lines
5.7 KiB
Swift
|
import Foundation
|
|||
|
|
|||
|
enum DNSChangeResponse: RawRepresentable, Codable {
|
|||
|
|
|||
|
/**
|
|||
|
The update was successful. You should not attempt another update until your IP address changes.
|
|||
|
|
|||
|
The response is followed by a list of the user’s IP addresses
|
|||
|
*/
|
|||
|
case success
|
|||
|
|
|||
|
/**
|
|||
|
The supplied IP address is already set for this host. You should not attempt another update until your IP address changes.
|
|||
|
|
|||
|
The response is followed by a list of the user’s IP addresses
|
|||
|
*/
|
|||
|
case noChange
|
|||
|
|
|||
|
/**
|
|||
|
The hostname doesn't exist, or doesn't have Dynamic DNS enabled.
|
|||
|
*/
|
|||
|
case invalidHostname
|
|||
|
|
|||
|
/**
|
|||
|
The username/password combination isn't valid for the specified host.
|
|||
|
*/
|
|||
|
case badAuthentication
|
|||
|
|
|||
|
/**
|
|||
|
The supplied hostname isn't a valid fully-qualified domain name.
|
|||
|
*/
|
|||
|
case notFullyQualifiedDomainName
|
|||
|
|
|||
|
/**
|
|||
|
Your Dynamic DNS client makes bad requests. Ensure the user agent is set in the request.
|
|||
|
*/
|
|||
|
case badUserAgent
|
|||
|
|
|||
|
/**
|
|||
|
Too many hosts (more than 20) specified in an update. Also returned if trying to update a round robin (which is not allowed).
|
|||
|
*/
|
|||
|
case tooManyHosts
|
|||
|
|
|||
|
/**
|
|||
|
Dynamic DNS access for the hostname has been blocked due to failure to interpret previous responses correctly.
|
|||
|
*/
|
|||
|
case abuse
|
|||
|
|
|||
|
/**
|
|||
|
An error happened on the server end. Wait 5 minutes and retry.
|
|||
|
*/
|
|||
|
case unexpectedServerError
|
|||
|
|
|||
|
/**
|
|||
|
A custom A or AAAA resource record conflicts with the update. Delete the indicated resource record within the DNS settings page and try the update again.
|
|||
|
*/
|
|||
|
case conflict
|
|||
|
|
|||
|
case noResponseString
|
|||
|
|
|||
|
case unknown(String)
|
|||
|
|
|||
|
/// The IP addresses reported after update are not the same as the requested
|
|||
|
case ipMismatch
|
|||
|
|
|||
|
init(rawValue: String) {
|
|||
|
switch rawValue {
|
|||
|
case "good": self = .success
|
|||
|
case "nochg": self = .noChange
|
|||
|
case "nohost": self = .invalidHostname
|
|||
|
case "badauth": self = .badAuthentication
|
|||
|
case "notfqdn": self = .notFullyQualifiedDomainName
|
|||
|
case "badagent": self = .badUserAgent
|
|||
|
case "abuse": self = .abuse
|
|||
|
case "numhost": self = .tooManyHosts
|
|||
|
case "911": self = .unexpectedServerError
|
|||
|
case "conflict": self = .conflict
|
|||
|
case "nostring": self = .noResponseString
|
|||
|
case "mismatch": self = .ipMismatch
|
|||
|
default: self = .unknown(rawValue)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var rawValue: String {
|
|||
|
switch self {
|
|||
|
case .success: return "good"
|
|||
|
case .noChange: return "nochg"
|
|||
|
case .invalidHostname: return "nohost"
|
|||
|
case .badAuthentication: return "badauth"
|
|||
|
case .notFullyQualifiedDomainName: return "notfqdn"
|
|||
|
case .badUserAgent: return "badagent"
|
|||
|
case .abuse: return "abuse"
|
|||
|
case .tooManyHosts: return "numhost"
|
|||
|
case .unexpectedServerError: return "911"
|
|||
|
case .conflict: return "conflict"
|
|||
|
case .noResponseString: return "nostring"
|
|||
|
case .ipMismatch: return "mismatch"
|
|||
|
case .unknown(let string): return string
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
init(line: String, expecting addresses: Set<String>) {
|
|||
|
let parts = line.components(separatedBy: " ").compactMap { $0.trimmed }
|
|||
|
self.init(rawValue: parts[0])
|
|||
|
if case .unknown = self {
|
|||
|
// Insert full response for better logging
|
|||
|
self = .unknown(line)
|
|||
|
return
|
|||
|
}
|
|||
|
guard isSuccessStatus else {
|
|||
|
return
|
|||
|
}
|
|||
|
guard Set(parts.dropFirst()) != addresses else {
|
|||
|
return
|
|||
|
}
|
|||
|
self = .ipMismatch
|
|||
|
}
|
|||
|
|
|||
|
var isSuccessStatus: Bool {
|
|||
|
switch self {
|
|||
|
case .success, .noChange, .noResponseString, .ipMismatch:
|
|||
|
// Treat no response and ip mismatch as success,
|
|||
|
// otherwise we risk spamming the server
|
|||
|
return true
|
|||
|
default:
|
|||
|
return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var isServerError: Bool {
|
|||
|
switch self {
|
|||
|
case .invalidHostname, .badAuthentication, .notFullyQualifiedDomainName, .badUserAgent, .abuse, .unexpectedServerError, .conflict, .tooManyHosts:
|
|||
|
return true
|
|||
|
default: return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var isClientError: Bool {
|
|||
|
switch self {
|
|||
|
case .noResponseString, .unknown, .ipMismatch:
|
|||
|
return true
|
|||
|
default: return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func delayTime(previous time: TimeInterval) -> TimeInterval {
|
|||
|
switch self {
|
|||
|
case .invalidHostname, .badAuthentication, .notFullyQualifiedDomainName, .badUserAgent, .conflict, .noChange:
|
|||
|
// Increase wait time by 10 minutes, up to 24 hours
|
|||
|
return min(86400, time + 600)
|
|||
|
case .abuse:
|
|||
|
// Increase wait time by one hour, until abuse is lifted
|
|||
|
return time + 3600
|
|||
|
case .unknown:
|
|||
|
// Don't spam on unknown errors
|
|||
|
return time + 3600
|
|||
|
default:
|
|||
|
// Try again next time
|
|||
|
return 0
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
extension DNSChangeResponse: CustomStringConvertible {
|
|||
|
|
|||
|
var description: String {
|
|||
|
switch self {
|
|||
|
case .success: return "success"
|
|||
|
case .noChange: return "No change"
|
|||
|
case .invalidHostname: return "Invalid hostname"
|
|||
|
case .badAuthentication: return "Bad authentication"
|
|||
|
case .notFullyQualifiedDomainName: return "Not FQDN"
|
|||
|
case .badUserAgent: return "Bad user agent"
|
|||
|
case .tooManyHosts: return "Too many hosts"
|
|||
|
case .abuse: return "Abuse"
|
|||
|
case .unexpectedServerError: return "Server error"
|
|||
|
case .conflict: return "Conflict"
|
|||
|
case .noResponseString: return "No response string"
|
|||
|
case .unknown(let string): return string
|
|||
|
case .ipMismatch: return "IP mismatch"
|
|||
|
}
|
|||
|
}
|
|||
|
}
|