use crate::common::{AnalysisResult, LastAnalysisStats, Votes};
use std::collections::HashMap;
#[cfg(feature = "chrono")]
use chrono::{
serde::{ts_seconds, ts_seconds_option},
DateTime, Utc,
};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DomainAttributes {
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds")]
pub last_modification_date: DateTime<Utc>,
#[cfg(not(feature = "chrono"))]
pub last_modification_date: u64,
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds_option", default)]
pub last_https_certificate_date: Option<DateTime<Utc>>,
#[cfg(not(feature = "chrono"))]
#[serde(default)]
pub last_https_certificate_date: Option<u64>,
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds")]
pub creation_date: DateTime<Utc>,
#[cfg(not(feature = "chrono"))]
pub creation_date: u64,
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds")]
pub whois_date: DateTime<Utc>,
#[cfg(not(feature = "chrono"))]
pub whois_date: u64,
pub whois: String,
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds_option", default)]
pub last_dns_records_date: Option<DateTime<Utc>>,
#[cfg(not(feature = "chrono"))]
#[serde(default)]
pub last_dns_records_date: Option<u64>,
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds_option", default)]
pub last_update_date: Option<DateTime<Utc>>,
#[cfg(not(feature = "chrono"))]
#[serde(default)]
pub last_update_date: Option<u64>,
pub jarm: String,
pub tld: String,
pub registrar: String,
#[serde(default)]
pub last_dns_records: Vec<DnsRecord>,
pub total_votes: Votes,
pub last_analysis_stats: LastAnalysisStats,
#[serde(default)]
pub last_analysis_results: HashMap<String, AnalysisResult>,
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds")]
pub last_analysis_date: DateTime<Utc>,
#[cfg(not(feature = "chrono"))]
pub last_analysis_date: u64,
pub reputation: u64,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
pub popularity_ranks: HashMap<String, PopularityRankEntry>,
#[serde(default)]
pub last_https_certificate: Option<crate::common::certs::SSLCertificate>,
#[serde(default)]
pub categories: HashMap<String, serde_json::Value>,
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds_option", default)]
pub expiration_date: Option<DateTime<Utc>>,
#[cfg(not(feature = "chrono"))]
pub expiration_date: Option<u64>,
#[serde(default)]
pub rdap: HashMap<String, serde_json::Value>,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DnsRecord {
#[serde(default)]
pub expire: Option<u64>,
#[serde(default)]
pub minimum: Option<u64>,
#[serde(rename = "type")]
pub record_type: DnsRecordType,
#[serde(default)]
pub refresh: Option<u64>,
#[serde(default)]
pub retry: Option<u64>,
#[serde(default)]
pub serial: Option<u64>,
#[serde(default)]
pub tag: Option<String>,
pub ttl: u64,
pub value: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum DnsRecordType {
#[serde(alias = "a")]
A,
#[serde(alias = "aaaa")]
AAAA,
#[serde(alias = "afsdb")]
AFSDB,
#[serde(alias = "apl")]
APL,
#[serde(alias = "caa")]
CAA,
#[serde(alias = "cdnskey")]
CDNSKEY,
#[serde(alias = "cds")]
CDS,
#[serde(alias = "cert")]
CERT,
#[serde(alias = "cname")]
CNAME,
#[serde(alias = "csync")]
CSYNC,
#[serde(alias = "dhcid")]
DHCID,
#[serde(alias = "dlv")]
DLV,
#[serde(alias = "dname")]
DNAME,
#[serde(alias = "dnskey")]
DNSKEY,
#[serde(alias = "ds")]
DS,
#[serde(alias = "eui48")]
EUI48,
#[serde(alias = "eui64")]
EUI64,
#[serde(alias = "hinfo")]
HINFO,
#[serde(alias = "hip")]
HIP,
#[serde(alias = "https")]
HTTPS,
#[serde(alias = "ipseckey")]
IPSECKEY,
#[serde(alias = "key")]
KEY,
#[serde(alias = "kx")]
KX,
#[serde(alias = "loc")]
LOC,
#[serde(alias = "mx")]
MX,
#[serde(alias = "naptr")]
NAPTR,
#[serde(alias = "ns")]
NS,
#[serde(alias = "nsec")]
NSEC,
#[serde(alias = "nsec3param")]
NSEC3PARAM,
#[serde(alias = "openpgpkey")]
OPENPGPKEY,
#[serde(alias = "ptr")]
PTR,
#[serde(alias = "rp")]
RP,
#[serde(alias = "rrsig")]
RRSIG,
#[serde(alias = "sig")]
SIG,
#[serde(alias = "smimea")]
SMIMEA,
#[serde(alias = "soa")]
SOA,
#[serde(alias = "srv")]
SRV,
#[serde(alias = "sshfp")]
SSHFP,
#[serde(alias = "svcb")]
SVCB,
#[serde(alias = "ta")]
TA,
#[serde(alias = "tkey")]
TKEY,
#[serde(alias = "tlsa")]
TLSA,
#[serde(alias = "tsig")]
TSIG,
#[serde(alias = "txt")]
TXT,
#[serde(alias = "uri")]
URI,
#[serde(alias = "zonemd")]
ZONEMD,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PopularityRankEntry {
#[cfg(feature = "chrono")]
#[serde(with = "ts_seconds")]
pub timestamp: DateTime<Utc>,
#[cfg(not(feature = "chrono"))]
pub timestamp: u64,
pub rank: u64,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::{ReportRequestResponse, ReportResponseHeader};
#[test]
fn haiku_org() {
const DOMAIN_REPORT: &str = include_str!("../../testdata/haikuorg.json");
let report: ReportRequestResponse<ReportResponseHeader<DomainAttributes>> =
serde_json::from_str(DOMAIN_REPORT).expect("failed to deserialize VT report");
let ReportRequestResponse::Data(report) = report else {
panic!("expected data");
};
eprintln!("Remaining fields: {}", report.attributes.extra.len());
eprintln!("{:?}", report.attributes.extra);
assert!(report.attributes.extra.is_empty());
#[cfg(feature = "chrono")]
{
assert!(report
.attributes
.last_https_certificate
.unwrap()
.validity
.not_before_to_chrono()
.is_ok());
}
}
}