krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

v1.4.0: DomainDig/Models.swift · raw

  1import Foundation
  2
  3// MARK: - DNS Models
  4
  5enum DNSRecordType: String, CaseIterable, Codable {
  6    case A
  7    case AAAA
  8    case MX
  9    case NS
 10    case TXT
 11    case CNAME
 12    case SOA
 13    case SRV
 14    case CAA
 15    case DS
 16
 17    var queryType: Int {
 18        switch self {
 19        case .A: return 1
 20        case .AAAA: return 28
 21        case .MX: return 15
 22        case .NS: return 2
 23        case .TXT: return 16
 24        case .CNAME: return 5
 25        case .SOA: return 6
 26        case .SRV: return 33
 27        case .CAA: return 257
 28        case .DS: return 43
 29        }
 30    }
 31
 32    var usesRawDataValue: Bool {
 33        switch self {
 34        case .TXT, .SOA, .DS:
 35            return true
 36        default:
 37            return false
 38        }
 39    }
 40}
 41
 42struct DNSRecord: Identifiable, Codable {
 43    var id = UUID()
 44    let value: String
 45    let ttl: Int
 46}
 47
 48struct DNSSection: Identifiable, Codable {
 49    var id = UUID()
 50    let recordType: DNSRecordType
 51    var records: [DNSRecord]
 52    var wildcardRecords: [DNSRecord] = []
 53    var dnssecSigned: Bool?
 54    var error: String?
 55}
 56
 57// MARK: - SSL Models
 58
 59struct SSLCertificateInfo: Codable {
 60    struct CertChainEntry: Codable {
 61        let subject: String
 62        let issuer: String
 63    }
 64
 65    let commonName: String
 66    let subjectAltNames: [String]
 67    let issuer: String
 68    let validFrom: Date
 69    let validUntil: Date
 70    let daysUntilExpiry: Int
 71    let chainDepth: Int
 72    let tlsVersion: String?
 73    let cipherSuite: String?
 74    let chain: [CertChainEntry]
 75
 76    init(
 77        commonName: String,
 78        subjectAltNames: [String],
 79        issuer: String,
 80        validFrom: Date,
 81        validUntil: Date,
 82        daysUntilExpiry: Int,
 83        chainDepth: Int,
 84        tlsVersion: String? = nil,
 85        cipherSuite: String? = nil,
 86        chain: [CertChainEntry] = []
 87    ) {
 88        self.commonName = commonName
 89        self.subjectAltNames = subjectAltNames
 90        self.issuer = issuer
 91        self.validFrom = validFrom
 92        self.validUntil = validUntil
 93        self.daysUntilExpiry = daysUntilExpiry
 94        self.chainDepth = chainDepth
 95        self.tlsVersion = tlsVersion
 96        self.cipherSuite = cipherSuite
 97        self.chain = chain
 98    }
 99
100    init(from decoder: Decoder) throws {
101        let container = try decoder.container(keyedBy: CodingKeys.self)
102        commonName = try container.decode(String.self, forKey: .commonName)
103        subjectAltNames = try container.decode([String].self, forKey: .subjectAltNames)
104        issuer = try container.decode(String.self, forKey: .issuer)
105        validFrom = try container.decode(Date.self, forKey: .validFrom)
106        validUntil = try container.decode(Date.self, forKey: .validUntil)
107        daysUntilExpiry = try container.decode(Int.self, forKey: .daysUntilExpiry)
108        chainDepth = try container.decode(Int.self, forKey: .chainDepth)
109        tlsVersion = try container.decodeIfPresent(String.self, forKey: .tlsVersion)
110        cipherSuite = try container.decodeIfPresent(String.self, forKey: .cipherSuite)
111        chain = try container.decodeIfPresent([CertChainEntry].self, forKey: .chain) ?? []
112    }
113}
114
115// MARK: - HTTP Headers Models
116
117struct HTTPHeader: Identifiable, Codable {
118    var id = UUID()
119    let name: String
120    let value: String
121
122    static let securityHeaders: Set<String> = [
123        "strict-transport-security",
124        "x-frame-options",
125        "x-content-type-options",
126        "content-security-policy",
127        "referrer-policy"
128    ]
129
130    var isSecurityHeader: Bool {
131        Self.securityHeaders.contains(name.lowercased())
132    }
133}
134
135// MARK: - Reachability Models
136
137struct PortReachability: Identifiable, Codable {
138    var id = UUID()
139    let port: UInt16
140    let reachable: Bool
141    let latencyMs: Int?
142}
143
144// MARK: - IP Geolocation Models
145
146struct IPGeolocation: Codable {
147    let ip: String
148    let city: String?
149    let region: String?
150    let country_name: String?
151    let org: String?
152    let latitude: Double?
153    let longitude: Double?
154}
155
156// MARK: - Email Security Models
157
158struct EmailSecurityResult: Codable {
159    let spf: EmailSecurityRecord
160    let dmarc: EmailSecurityRecord
161    let dkim: EmailSecurityRecord
162}
163
164struct EmailSecurityRecord: Codable {
165    let found: Bool
166    let value: String?
167}
168
169// MARK: - Redirect Chain Models
170
171struct RedirectHop: Identifiable, Codable {
172    var id = UUID()
173    let stepNumber: Int
174    let statusCode: Int
175    let url: String
176    let isFinal: Bool
177}
178
179// MARK: - Port Scan Models
180
181struct PortScanResult: Identifiable, Codable {
182    var id = UUID()
183    let port: UInt16
184    let service: String
185    let open: Bool
186}
187
188// MARK: - History Models
189
190struct HistoryEntry: Identifiable, Codable {
191    var id = UUID()
192    let domain: String
193    let timestamp: Date
194    let dnsSections: [DNSSection]
195    let sslInfo: SSLCertificateInfo?
196    let httpHeaders: [HTTPHeader]
197    let reachabilityResults: [PortReachability]
198    let ipGeolocation: IPGeolocation?
199    var emailSecurity: EmailSecurityResult?
200    var ptrRecord: String?
201    var redirectChain: [RedirectHop]
202    var portScanResults: [PortScanResult]
203    var hstsPreloaded: Bool?
204
205    init(domain: String, timestamp: Date, dnsSections: [DNSSection],
206         sslInfo: SSLCertificateInfo?, httpHeaders: [HTTPHeader],
207         reachabilityResults: [PortReachability], ipGeolocation: IPGeolocation?,
208         emailSecurity: EmailSecurityResult? = nil, ptrRecord: String? = nil,
209         redirectChain: [RedirectHop] = [], portScanResults: [PortScanResult] = [],
210         hstsPreloaded: Bool? = nil) {
211        self.domain = domain
212        self.timestamp = timestamp
213        self.dnsSections = dnsSections
214        self.sslInfo = sslInfo
215        self.httpHeaders = httpHeaders
216        self.reachabilityResults = reachabilityResults
217        self.ipGeolocation = ipGeolocation
218        self.emailSecurity = emailSecurity
219        self.ptrRecord = ptrRecord
220        self.redirectChain = redirectChain
221        self.portScanResults = portScanResults
222        self.hstsPreloaded = hstsPreloaded
223    }
224
225    init(from decoder: Decoder) throws {
226        let container = try decoder.container(keyedBy: CodingKeys.self)
227        id = try container.decodeIfPresent(UUID.self, forKey: .id) ?? UUID()
228        domain = try container.decode(String.self, forKey: .domain)
229        timestamp = try container.decode(Date.self, forKey: .timestamp)
230        dnsSections = try container.decode([DNSSection].self, forKey: .dnsSections)
231        sslInfo = try container.decodeIfPresent(SSLCertificateInfo.self, forKey: .sslInfo)
232        httpHeaders = try container.decode([HTTPHeader].self, forKey: .httpHeaders)
233        reachabilityResults = try container.decode([PortReachability].self, forKey: .reachabilityResults)
234        ipGeolocation = try container.decodeIfPresent(IPGeolocation.self, forKey: .ipGeolocation)
235        emailSecurity = try container.decodeIfPresent(EmailSecurityResult.self, forKey: .emailSecurity)
236        ptrRecord = try container.decodeIfPresent(String.self, forKey: .ptrRecord)
237        redirectChain = try container.decodeIfPresent([RedirectHop].self, forKey: .redirectChain) ?? []
238        portScanResults = try container.decodeIfPresent([PortScanResult].self, forKey: .portScanResults) ?? []
239        hstsPreloaded = try container.decodeIfPresent(Bool.self, forKey: .hstsPreloaded)
240    }
241}
242
243// MARK: - Cloudflare DNS-over-HTTPS Response
244
245struct CloudflareDNSResponse: Decodable {
246    let Status: Int
247    let Answer: [CloudflareDNSAnswer]?
248
249    struct CloudflareDNSAnswer: Decodable {
250        let name: String
251        let type: Int
252        let TTL: Int
253        let data: String
254    }
255}