diff options
Diffstat (limited to 'DomainDigCLI.swift')
| -rw-r--r-- | DomainDigCLI.swift | 208 |
1 files changed, 205 insertions, 3 deletions
diff --git a/DomainDigCLI.swift b/DomainDigCLI.swift index d0a4d86..a6d2cf3 100644 --- a/DomainDigCLI.swift +++ b/DomainDigCLI.swift @@ -6,12 +6,17 @@ struct DomainDigCLI { let arguments = Array(CommandLine.arguments.dropFirst()) guard let command = CommandLine.arguments.first else { - fputs("usage: domaindig <domain> [--json]\n", stderr) + fputs("usage: domaindig <domain> [--json] [--ownership-history] [--dns-history] [--extended-subdomains] [--pricing] [--show-usage]\n", stderr) Foundation.exit(1) } _ = command let wantsJSON = arguments.contains("--json") || arguments.contains("-j") + let wantsOwnershipHistory = arguments.contains("--ownership-history") + let wantsDNSHistory = arguments.contains("--dns-history") + let wantsExtendedSubdomains = arguments.contains("--extended-subdomains") + let wantsPricing = arguments.contains("--pricing") + let wantsUsage = arguments.contains("--show-usage") let domains = arguments.filter { !$0.hasPrefix("-") } let requestedDomains = domains @@ -19,18 +24,29 @@ struct DomainDigCLI { .filter { !$0.isEmpty } guard !requestedDomains.isEmpty else { - fputs("usage: domaindig <domain> [--json]\n", stderr) + fputs("usage: domaindig <domain> [--json] [--ownership-history] [--dns-history] [--extended-subdomains] [--pricing] [--show-usage]\n", stderr) Foundation.exit(1) } let inspectionService = DomainInspectionService() + let reportBuilder = DomainReportBuilder() var reports: [DomainReport] = [] var seen = Set<String>() + var usageImpact: [String] = [] for domain in requestedDomains { let normalizedDomain = domain.lowercased() guard seen.insert(normalizedDomain).inserted else { continue } - reports.append(await inspectionService.inspect(domain: domain)) + let snapshot = await inspectionService.inspectSnapshot(domain: domain) + let enrichedSnapshot = await enrichSnapshot( + snapshot, + wantsOwnershipHistory: wantsOwnershipHistory, + wantsDNSHistory: wantsDNSHistory, + wantsExtendedSubdomains: wantsExtendedSubdomains, + wantsPricing: wantsPricing, + usageImpact: &usageImpact + ) + reports.append(reportBuilder.build(from: enrichedSnapshot)) } do { @@ -47,6 +63,9 @@ struct DomainDigCLI { title: "DomainDig Batch Report" ) } + if wantsUsage, !usageImpact.isEmpty { + FileHandle.standardError.write(Data(("Data+ usage impact: " + usageImpact.joined(separator: ", ") + "\n").utf8)) + } FileHandle.standardOutput.write(data) if data.last != 0x0A { FileHandle.standardOutput.write(Data([0x0A])) @@ -56,4 +75,187 @@ struct DomainDigCLI { Foundation.exit(1) } } + + private static func enrichSnapshot( + _ snapshot: LookupSnapshot, + wantsOwnershipHistory: Bool, + wantsDNSHistory: Bool, + wantsExtendedSubdomains: Bool, + wantsPricing: Bool, + usageImpact: inout [String] + ) async -> LookupSnapshot { + guard FeatureAccessService.currentTier == .dataPlus else { + return snapshot + } + + let historyEntries = loadHistoryEntries() + var ownershipHistory = snapshot.ownershipHistory + var ownershipHistoryError = snapshot.ownershipHistoryError + var dnsHistory = snapshot.dnsHistory + var dnsHistoryError = snapshot.dnsHistoryError + var extendedSubdomains = snapshot.extendedSubdomains + var extendedSubdomainsError = snapshot.extendedSubdomainsError + var domainPricing = snapshot.domainPricing + var domainPricingError = snapshot.domainPricingError + + if wantsOwnershipHistory, + await UsageCreditService.shared.canUse(.ownershipHistory) { + let outcome = await ExternalDataService.shared.ownershipHistory( + domain: snapshot.domain, + currentOwnership: snapshot.ownership, + historyEntries: historyEntries + ) + switch outcome.value { + case let .success(events): + ownershipHistory = events + ownershipHistoryError = nil + if outcome.source != .cached { + _ = await UsageCreditService.shared.consume(.ownershipHistory) + usageImpact.append("ownership history -1") + } + case let .empty(message): + ownershipHistoryError = message + if outcome.source != .cached { + _ = await UsageCreditService.shared.consume(.ownershipHistory) + usageImpact.append("ownership history -1") + } + case let .error(message): + ownershipHistoryError = message + } + } + + if wantsDNSHistory, + await UsageCreditService.shared.canUse(.dnsHistory) { + let outcome = await ExternalDataService.shared.dnsHistory( + domain: snapshot.domain, + dnsSections: snapshot.dnsSections, + historyEntries: historyEntries + ) + switch outcome.value { + case let .success(events): + dnsHistory = events + dnsHistoryError = nil + if outcome.source != .cached { + _ = await UsageCreditService.shared.consume(.dnsHistory) + usageImpact.append("dns history -1") + } + case let .empty(message): + dnsHistoryError = message + if outcome.source != .cached { + _ = await UsageCreditService.shared.consume(.dnsHistory) + usageImpact.append("dns history -1") + } + case let .error(message): + dnsHistoryError = message + } + } + + if wantsExtendedSubdomains, + await UsageCreditService.shared.canUse(.extendedSubdomains) { + let outcome = await ExternalDataService.shared.extendedSubdomains( + domain: snapshot.domain, + existing: snapshot.subdomains + ) + switch outcome.value { + case let .success(results): + extendedSubdomains = results + extendedSubdomainsError = nil + if outcome.source != .cached { + _ = await UsageCreditService.shared.consume(.extendedSubdomains) + usageImpact.append("extended subdomains -1") + } + case let .empty(message): + extendedSubdomainsError = message + if outcome.source != .cached { + _ = await UsageCreditService.shared.consume(.extendedSubdomains) + usageImpact.append("extended subdomains -1") + } + case let .error(message): + extendedSubdomainsError = message + } + } + + if wantsPricing { + let outcome = await ExternalDataService.shared.pricing(domain: snapshot.domain) + switch outcome.value { + case let .success(pricing): + domainPricing = pricing + domainPricingError = nil + case let .empty(message), let .error(message): + domainPricingError = message + } + } + + return LookupSnapshot( + historyEntryID: snapshot.historyEntryID, + domain: snapshot.domain, + timestamp: snapshot.timestamp, + trackedDomainID: snapshot.trackedDomainID, + note: snapshot.note, + appVersion: snapshot.appVersion, + resolverDisplayName: snapshot.resolverDisplayName, + resolverURLString: snapshot.resolverURLString, + dataSources: snapshot.dataSources, + provenanceBySection: snapshot.provenanceBySection, + availabilityConfidence: snapshot.availabilityConfidence, + ownershipConfidence: snapshot.ownershipConfidence, + subdomainConfidence: snapshot.subdomainConfidence, + emailSecurityConfidence: snapshot.emailSecurityConfidence, + geolocationConfidence: snapshot.geolocationConfidence, + errorDetails: snapshot.errorDetails, + isPartialSnapshot: snapshot.isPartialSnapshot, + validationIssues: snapshot.validationIssues, + totalLookupDurationMs: snapshot.totalLookupDurationMs, + dnsSections: snapshot.dnsSections, + dnsError: snapshot.dnsError, + availabilityResult: snapshot.availabilityResult, + suggestions: snapshot.suggestions, + sslInfo: snapshot.sslInfo, + sslError: snapshot.sslError, + hstsPreloaded: snapshot.hstsPreloaded, + httpHeaders: snapshot.httpHeaders, + httpSecurityGrade: snapshot.httpSecurityGrade, + httpStatusCode: snapshot.httpStatusCode, + httpResponseTimeMs: snapshot.httpResponseTimeMs, + httpProtocol: snapshot.httpProtocol, + http3Advertised: snapshot.http3Advertised, + httpHeadersError: snapshot.httpHeadersError, + reachabilityResults: snapshot.reachabilityResults, + reachabilityError: snapshot.reachabilityError, + ipGeolocation: snapshot.ipGeolocation, + ipGeolocationError: snapshot.ipGeolocationError, + emailSecurity: snapshot.emailSecurity, + emailSecurityError: snapshot.emailSecurityError, + ownership: snapshot.ownership, + ownershipError: snapshot.ownershipError, + ownershipHistory: ownershipHistory, + ownershipHistoryError: ownershipHistoryError, + ptrRecord: snapshot.ptrRecord, + ptrError: snapshot.ptrError, + redirectChain: snapshot.redirectChain, + redirectChainError: snapshot.redirectChainError, + subdomains: snapshot.subdomains, + subdomainsError: snapshot.subdomainsError, + extendedSubdomains: extendedSubdomains, + extendedSubdomainsError: extendedSubdomainsError, + dnsHistory: dnsHistory, + dnsHistoryError: dnsHistoryError, + domainPricing: domainPricing, + domainPricingError: domainPricingError, + portScanResults: snapshot.portScanResults, + portScanError: snapshot.portScanError, + changeSummary: snapshot.changeSummary, + resultSource: snapshot.resultSource, + cachedSections: snapshot.cachedSections, + statusMessage: snapshot.statusMessage + ) + } + + private static func loadHistoryEntries() -> [HistoryEntry] { + guard let data = UserDefaults.standard.data(forKey: "lookupHistory"), + let entries = try? JSONDecoder().decode([HistoryEntry].self, from: data) else { + return [] + } + return entries + } } |
