rdap_client/
parser.rs

1use chrono::{DateTime, FixedOffset, Offset, TimeZone, Utc};
2use serde::de::{IntoDeserializer, SeqAccess, Unexpected, Visitor};
3use serde::ser::SerializeSeq;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::convert::TryFrom;
6use std::fmt;
7use std::fmt::Write;
8use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
9use std::slice::Iter;
10use std::str::FromStr;
11
12fn deserialize_string_lowercase<'de, D>(deserializer: D) -> Result<String, D::Error>
13where
14    D: Deserializer<'de>,
15{
16    let mut string = String::deserialize(deserializer)?;
17    // Just convert in case that string contains uppercase character
18    // This solution is about 70% faster than convert it in all cases
19    if string.chars().any(|c| c.is_uppercase()) {
20        string = string.to_lowercase();
21    }
22    Ok(string)
23}
24
25/// Because not all RDAP servers are RFC 7483 complaint (they use datetime in formats that are
26/// incompatible with RFC 3339), this method can parse all kinds of different format used in domains
27/// RDAP servers:
28/// - RFC 3339 format
29/// - %Y-%m-%dT%H:%M:%S
30/// - %Y-%m-%dT%H:%M:%SZ%z
31/// - %Y-%m-%d %H:%M:%S
32fn deserialize_datetime<'de, D>(deserializer: D) -> Result<DateTime<FixedOffset>, D::Error>
33where
34    D: Deserializer<'de>,
35{
36    let string = String::deserialize(deserializer)?;
37
38    DateTime::parse_from_rfc3339(&string)
39        .or_else(|_| {
40            if string.contains('T') {
41                Utc.datetime_from_str(&string, "%Y-%m-%dT%H:%M:%S")
42                    .map(|d| d.with_timezone(&Utc.fix()))
43                    .or_else(|_| DateTime::parse_from_str(&string, "%Y-%m-%dT%H:%M:%SZ%z"))
44            } else {
45                Utc.datetime_from_str(&string, "%Y-%m-%d %H:%M:%S")
46                    .map(|d| d.with_timezone(&Utc.fix())) // for `xn--rhqv96g` domain
47            }
48        })
49        .map_err(serde::de::Error::custom)
50}
51
52/// Two letters (usually ISO 3166-1) country code.
53// Some registries uses codes that are not ISO 3166-1 countries (for example RIPe uses 'EU'
54// as country), so we store that string as two bytes and not as for example isocountry::CountryCode.
55#[derive(PartialEq)]
56pub struct CountryCode([u8; 2]);
57
58impl FromStr for CountryCode {
59    type Err = &'static str;
60
61    fn from_str(string: &str) -> Result<Self, Self::Err> {
62        let bytes = string.as_bytes();
63        if !bytes.is_ascii() || bytes.len() != 2 {
64            return Err("string is not two letter ascii");
65        }
66        Ok(Self([
67            bytes[0].to_ascii_uppercase(),
68            bytes[1].to_ascii_uppercase(),
69        ]))
70    }
71}
72
73impl fmt::Debug for CountryCode {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
75        write!(f, "{}", self)
76    }
77}
78
79impl fmt::Display for CountryCode {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
81        f.write_char(self.0[0] as char)?;
82        f.write_char(self.0[1] as char)
83    }
84}
85
86impl Serialize for CountryCode {
87    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
88    where
89        S: Serializer,
90    {
91        let string = std::str::from_utf8(&self.0).unwrap(); // should never fail
92        serializer.serialize_str(string)
93    }
94}
95
96impl<'de> Deserialize<'de> for CountryCode {
97    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98    where
99        D: Deserializer<'de>,
100    {
101        struct CountryCodeVisitor;
102
103        impl<'de> Visitor<'de> for CountryCodeVisitor {
104            type Value = CountryCode;
105
106            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
107                write!(formatter, "expecting a two letters country code")
108            }
109
110            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
111            where
112                E: serde::de::Error,
113            {
114                CountryCode::from_str(v).map_err(|_| {
115                    serde::de::Error::invalid_value(serde::de::Unexpected::Str(v), &self)
116                })
117            }
118        }
119
120        deserializer.deserialize_str(CountryCodeVisitor)
121    }
122}
123
124/// https://tools.ietf.org/html/rfc7483#section-4.2
125#[derive(Serialize, Deserialize, Debug)]
126pub struct Link {
127    pub value: Option<String>,
128    pub rel: Option<String>,
129    pub href: String,
130    pub hreflang: Option<Vec<String>>,
131    pub title: Option<String>,
132    pub media: Option<String>,
133    #[serde(rename = "type")]
134    pub typ: Option<String>,
135}
136
137/// Value signifying the relationship an object would have with its closest containing object.
138/// Values come from [RFC 7483] and [RDAP JSON Values].
139///
140/// [RFC 7483]: https://tools.ietf.org/html/rfc7483#section-10.2.4
141/// [RDAP JSON Values]: https://www.iana.org/assignments/rdap-json-values/rdap-json-values.xhtml
142#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
143#[serde(rename_all = "lowercase")]
144#[serde(remote = "Role")]
145pub enum Role {
146    /// The entity object instance is the registrant of the registration. In some registries, this is known as a maintainer.
147    Registrant,
148    /// The entity object instance is a technical contact for the registration.
149    Technical,
150    /// The entity object instance is an administrative contact for the registration.
151    Administrative,
152    /// The entity object instance handles network abuse issues on behalf of the registrant of the registration.
153    Abuse,
154    /// The entity object instance handles payment and billing issues on behalf of the registrant of the registration.
155    Billing,
156    /// The entity object instance represents the authority responsible for the registration in the registry.
157    Registrar,
158    /// The entity object instance represents a third party through which the registration was conducted (i.e., not the registry or registrar).
159    Reseller,
160    /// The entity object instance represents a domain policy sponsor, such as an ICANN-approved sponsor.
161    Sponsor,
162    /// The entity object instance represents a proxy for another entity object, such as a registrant.
163    Proxy,
164    /// An entity object instance designated to receive notifications about association object instances.
165    Notifications,
166    /// The entity object instance handles communications related to a network operations center (NOC).
167    Noc,
168    /// Non standard
169    Routing,
170    /// Non standard role used by ARIN registry.
171    Dns,
172    /// Non standard role used by RIPE NCC for reverse domain response. See [RIPE NCC RDAP Implementation](https://github.com/RIPE-NCC/whois/blob/master/README.RDAP.md#custom-zone-role-for-domain-objects).
173    Zone,
174}
175
176impl<'de> Deserialize<'de> for Role {
177    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178    where
179        D: Deserializer<'de>,
180    {
181        let s = deserialize_string_lowercase(deserializer)?;
182
183        // Non standard for 'tirol' domain - https://rdap.ryce-rsp.com/rdap/
184        match s.as_str() {
185            "tech" => Ok(Self::Technical),
186            "admin" => Ok(Self::Administrative),
187            _ => Self::deserialize(s.into_deserializer()),
188        }
189    }
190}
191
192impl Serialize for Role {
193    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
194    where
195        S: Serializer,
196    {
197        Self::serialize(self, serializer)
198    }
199}
200
201/// https://tools.ietf.org/html/rfc7483#section-4.8
202#[derive(Serialize, Deserialize, Debug)]
203pub struct PublicId {
204    #[serde(rename = "type")]
205    pub typ: String,
206    pub identifier: String,
207}
208
209#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
210#[serde(rename_all = "lowercase")]
211pub enum JCardType {
212    Vcard,
213}
214
215/// https://tools.ietf.org/html/rfc6350#section-4
216#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
217#[serde(rename_all = "kebab-case")]
218#[serde(remote = "JCardItemDataType")]
219pub enum JCardItemDataType {
220    Text,
221    TextList,
222    DateList,
223    TimeList,
224    DateTimeList,
225    DateAndOrTimeList,
226    TimestampList,
227    Boolean,
228    IntegerList,
229    FloatList,
230    Uri,
231    UtcOffset,
232    LanguageTag,
233    IanaValuespec,
234    /// See https://tools.ietf.org/html/rfc7095#section-5
235    Unknown,
236}
237
238impl<'de> Deserialize<'de> for JCardItemDataType {
239    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
240    where
241        D: Deserializer<'de>,
242    {
243        let s = deserialize_string_lowercase(deserializer)?;
244        Self::deserialize(s.into_deserializer())
245    }
246}
247
248impl Serialize for JCardItemDataType {
249    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
250    where
251        S: Serializer,
252    {
253        Self::serialize(self, serializer)
254    }
255}
256
257#[derive(Debug)]
258pub struct JCardItem {
259    pub property_name: String,
260    pub parameters: serde_json::Map<String, serde_json::Value>,
261    pub type_identifier: JCardItemDataType,
262    pub values: Vec<serde_json::Value>,
263}
264
265impl<'de> Deserialize<'de> for JCardItem {
266    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
267    where
268        D: Deserializer<'de>,
269    {
270        struct JCardItemVisitor;
271
272        impl<'de> Visitor<'de> for JCardItemVisitor {
273            type Value = JCardItem;
274
275            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
276                formatter.write_str("a sequence")
277            }
278
279            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
280            where
281                A: SeqAccess<'de>,
282            {
283                let invalid_length =
284                    |size| serde::de::Error::invalid_length(size, &"at least four elements");
285
286                // Property name must be lowercase string, see https://tools.ietf.org/html/rfc7095#section-3.3
287                let mut property_name: String =
288                    seq.next_element()?.ok_or_else(|| invalid_length(0))?;
289                if property_name.chars().any(|c| c.is_uppercase()) {
290                    property_name = property_name.to_lowercase();
291                }
292
293                let parameters = seq.next_element()?.ok_or_else(|| invalid_length(1))?;
294                let type_identifier = seq.next_element()?.ok_or_else(|| invalid_length(2))?;
295
296                let mut values = vec![];
297                while let Some(value) = seq.next_element()? {
298                    values.push(value);
299                }
300
301                if values.is_empty() {
302                    return Err(invalid_length(3));
303                }
304
305                Ok(JCardItem {
306                    property_name,
307                    parameters,
308                    type_identifier,
309                    values,
310                })
311            }
312        }
313
314        deserializer.deserialize_seq(JCardItemVisitor)
315    }
316}
317
318impl Serialize for JCardItem {
319    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
320    where
321        S: Serializer,
322    {
323        let mut seq = serializer.serialize_seq(Some(3 + self.values.len()))?;
324        seq.serialize_element(&self.property_name)?;
325        seq.serialize_element(&self.parameters)?;
326        seq.serialize_element(&self.type_identifier)?;
327        for value in &self.values {
328            seq.serialize_element(value)?;
329        }
330        seq.end()
331    }
332}
333
334/// https://tools.ietf.org/html/rfc7095
335#[derive(Serialize, Deserialize, Debug)]
336pub struct JCard(JCardType, Vec<JCardItem>);
337
338impl JCard {
339    pub fn typ(&self) -> JCardType {
340        self.0
341    }
342
343    pub fn items(&self) -> &Vec<JCardItem> {
344        &self.1
345    }
346
347    /// name as lowercase string.
348    pub fn items_by_name(&self, name: &str) -> Vec<&JCardItem> {
349        self.1.iter().filter(|p| p.property_name == name).collect()
350    }
351}
352
353#[derive(Serialize, Deserialize, Debug)]
354#[serde(rename_all = "camelCase")]
355pub struct Entity {
356    pub handle: Option<String>,
357    pub vcard_array: Option<JCard>,
358    pub roles: Option<Vec<Role>>,
359    pub public_ids: Option<Vec<PublicId>>,
360    pub entities: Option<Vec<Entity>>,
361    pub remarks: Option<NoticesOrRemarks>,
362    pub links: Option<Vec<Link>>,
363    pub events: Option<Events>,
364    pub as_event_actor: Option<Events>,
365    pub status: Option<Vec<Status>>,
366    pub port43: Option<String>,
367    pub lang: Option<String>,
368    pub object_class_name: String,
369}
370
371/// https://tools.ietf.org/html/rfc7483#section-10.2.2
372#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
373#[serde(rename_all = "lowercase")]
374pub enum Status {
375    Validated,
376    #[serde(rename = "renew prohibited")]
377    RenewProhibited,
378    #[serde(rename = "update prohibited")]
379    UpdateProhibited,
380    #[serde(rename = "transfer prohibited")]
381    TransferProhibited,
382    #[serde(rename = "delete prohibited")]
383    DeleteProhibited,
384    Proxy,
385    Private,
386    Removed,
387    Obscured,
388    Associated,
389    Active,
390    Inactive,
391    Locked,
392    #[serde(rename = "pending create")]
393    PendingCreate,
394    #[serde(rename = "pending renew")]
395    PendingRenew,
396    #[serde(rename = "pending transfer")]
397    PendingTransfer,
398    #[serde(rename = "pending update")]
399    PendingUpdate,
400    #[serde(rename = "pending delete")]
401    PendingDelete,
402    // From RFC8056
403    #[serde(rename = "add period")]
404    AddPeriod,
405    #[serde(rename = "auto renew period")]
406    AutoRenewPeriod,
407    #[serde(rename = "client delete prohibited")]
408    ClientDeleteProhibited,
409    #[serde(rename = "client hold")]
410    ClientHold,
411    #[serde(rename = "client renew prohibited")]
412    ClientRenewProhibited,
413    #[serde(rename = "client transfer prohibited")]
414    ClientTransferProhibited,
415    #[serde(rename = "client update prohibited")]
416    ClientUpdateProhibited,
417    #[serde(rename = "pending restore")]
418    PendingRestore,
419    #[serde(rename = "redemption period")]
420    RedemptionPeriod,
421    #[serde(rename = "renew period")]
422    RenewPeriod,
423    #[serde(rename = "server delete prohibited")]
424    ServerDeleteProhibited,
425    #[serde(rename = "server renew prohibited")]
426    ServerRenewProhibited,
427    #[serde(rename = "server transfer prohibited")]
428    ServerTransferProhibited,
429    #[serde(rename = "server update prohibited")]
430    ServerUpdateProhibited,
431    #[serde(rename = "server hold")]
432    ServerHold,
433    #[serde(rename = "transfer period")]
434    TransferPeriod,
435    // Non standard
436    /// Non standard 'flir' domain registry status for nameservers.
437    Ok,
438}
439
440#[derive(Serialize, Deserialize, Debug)]
441pub struct IpAddresses {
442    pub v4: Option<Vec<Ipv4Addr>>,
443    pub v6: Option<Vec<Ipv6Addr>>,
444}
445
446/// https://tools.ietf.org/html/rfc7483#section-5.2
447#[derive(Serialize, Deserialize, Debug)]
448#[serde(rename_all = "camelCase")]
449pub struct Nameserver {
450    pub handle: Option<String>,
451    pub ldh_name: String,
452    pub unicode_name: Option<String>,
453    pub ip_addresses: Option<IpAddresses>,
454    pub entities: Option<Vec<Entity>>,
455    pub status: Option<Vec<Status>>,
456    pub remarks: Option<NoticesOrRemarks>,
457    pub notices: Option<NoticesOrRemarks>,
458    pub links: Option<Vec<Link>>,
459    pub object_class_name: String,
460}
461
462/// https://tools.ietf.org/html/rfc7483#section-10.2.3 and https://www.iana.org/assignments/rdap-json-values/rdap-json-values.xhtml
463#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
464#[serde(rename_all = "lowercase")]
465#[serde(remote = "EventAction")]
466pub enum EventAction {
467    Registration,
468    Reregistration,
469    #[serde(rename = "last changed")]
470    LastChanged,
471    Expiration,
472    Deletion,
473    Reinstantiation,
474    Transfer,
475    Locked,
476    Unlocked,
477    // Extensions
478    #[serde(rename = "last update of RDAP database")]
479    /// From 'icann_rdap_response_profile_0' extension.
480    LastUpdateOfRdapDatabase,
481    #[serde(rename = "registrar expiration")]
482    /// From 'icann_rdap_response_profile_0' extension.
483    RegistrarExpiration,
484    #[serde(rename = "enum validation expiration")]
485    /// From 'fred' extension.
486    EnumValidationExpiration,
487    // Non standard
488    #[serde(rename = "delegation sign check")]
489    /// Non standard value from `final` domain RDAP.
490    DelegationSignCheck,
491    #[serde(rename = "soft expiration")]
492    /// Non standard value from `is` domain RDAP.
493    SoftExpiration,
494    #[serde(rename = "last correct delegation sign check")]
495    /// Non standard value from `br` domain RDAP.
496    LastCorrectDelegationSignCheck,
497}
498
499impl<'de> Deserialize<'de> for EventAction {
500    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
501    where
502        D: Deserializer<'de>,
503    {
504        let s = deserialize_string_lowercase(deserializer)?;
505        if s == "last update of rdap database" {
506            // Because original string is converted to lowercase and the original value contains
507            // uppercase word 'RDAP', we need to compare this value manually.
508            Ok(Self::LastUpdateOfRdapDatabase)
509        } else {
510            Self::deserialize(s.into_deserializer())
511        }
512    }
513}
514
515impl Serialize for EventAction {
516    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
517    where
518        S: Serializer,
519    {
520        Self::serialize(self, serializer)
521    }
522}
523
524/// https://tools.ietf.org/html/rfc7483#section-4.5
525#[derive(Serialize, Deserialize, Debug)]
526#[serde(rename_all = "camelCase")]
527pub struct Event {
528    pub event_actor: Option<String>,
529    pub event_action: EventAction,
530    #[serde(deserialize_with = "deserialize_datetime")]
531    pub event_date: DateTime<FixedOffset>,
532    pub links: Option<Link>,
533}
534
535#[derive(Serialize, Deserialize, Debug)]
536pub struct Events(Vec<Event>);
537
538impl Events {
539    pub fn action_date(&self, action: EventAction) -> Option<DateTime<FixedOffset>> {
540        self.0
541            .iter()
542            .find(|p| p.event_action == action)
543            .map(|e| e.event_date)
544    }
545}
546
547impl<'a> IntoIterator for &'a Events {
548    type Item = &'a Event;
549    type IntoIter = Iter<'a, Event>;
550
551    fn into_iter(self) -> Self::IntoIter {
552        self.0.iter()
553    }
554}
555
556/// https://tools.ietf.org/html/rfc7483#section-10.2.1 and https://www.iana.org/assignments/rdap-json-values/rdap-json-values.xhtml
557#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
558#[serde(remote = "NoticeOrRemarkType")]
559pub enum NoticeOrRemarkType {
560    #[serde(rename = "result set truncated due to authorization")]
561    ResultSetTruncatedDueToAuthorization,
562    #[serde(rename = "result set truncated due to excessive load")]
563    ResultSetTruncatedDueToExcessiveLoad,
564    #[serde(rename = "result set truncated due to unexplainable reasons")]
565    ResultSetTruncatedDueToUnexplainableReasons,
566    #[serde(rename = "object truncated due to authorization")]
567    ObjectTruncatedDueToAuthorization,
568    #[serde(rename = "object truncated due to excessive load")]
569    ObjectTruncatedDueToExcessiveLoad,
570    #[serde(rename = "object truncated due to unexplainable reasons")]
571    ObjectTruncatedDueToUnexplainableReasons,
572    // Extensions
573    #[serde(rename = "object redacted due to authorization")]
574    /// Value from 'icann_rdap_response_profile_0' extension.
575    ObjectRedactedDueToAuthorization,
576    // Non standards
577    #[serde(rename = "object truncated due to server policy")]
578    ObjectTruncatedDueToServerPolicy,
579    #[serde(rename = "response truncated due to authorization")]
580    /// Non standard value from 'abudhabi' domain registry.
581    ResponseTruncatedDueToAuthorization,
582}
583
584impl<'de> Deserialize<'de> for NoticeOrRemarkType {
585    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
586    where
587        D: Deserializer<'de>,
588    {
589        let s = deserialize_string_lowercase(deserializer)?;
590        if s == "object redacted due to authorization." {
591            // `lat` domain registry contains typo and value ends with dot :/
592            Ok(Self::ObjectRedactedDueToAuthorization)
593        } else {
594            Self::deserialize(s.into_deserializer())
595        }
596    }
597}
598
599impl Serialize for NoticeOrRemarkType {
600    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
601    where
602        S: Serializer,
603    {
604        Self::serialize(self, serializer)
605    }
606}
607
608#[derive(Serialize, Deserialize, Debug)]
609pub struct NoticeOrRemark {
610    pub title: Option<String>,
611    #[serde(rename = "type")]
612    pub typ: Option<NoticeOrRemarkType>,
613    pub description: Option<Vec<String>>,
614    pub links: Option<Vec<Link>>,
615}
616
617#[derive(Serialize, Deserialize, Debug)]
618pub struct NoticesOrRemarks(Vec<NoticeOrRemark>);
619
620impl NoticesOrRemarks {
621    pub fn description_by_title(&self, title: &str) -> Option<&Vec<String>> {
622        for remark in self.0.iter().filter(|p| p.description.is_some()) {
623            if let Some(t) = &remark.title {
624                if title.eq_ignore_ascii_case(t.as_str()) {
625                    return remark.description.as_ref();
626                }
627            } else if title == "remarks" {
628                return remark.description.as_ref();
629            }
630        }
631
632        None
633    }
634}
635
636impl<'a> IntoIterator for &'a NoticesOrRemarks {
637    type Item = &'a NoticeOrRemark;
638    type IntoIter = Iter<'a, NoticeOrRemark>;
639
640    fn into_iter(self) -> Self::IntoIter {
641        self.0.iter()
642    }
643}
644
645/// An enum signifying the IP protocol version of the network: "v4" signifies an IPv4 network,
646/// and "v6" signifies an IPv6 network.
647#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
648#[serde(rename_all = "camelCase")]
649pub enum IpVersion {
650    V4,
651    V6,
652}
653
654/// From 'cidr0' extension. https://bitbucket.org/nroecg/nro-rdap-cidr/src/master/nro-rdap-cidr.txt
655#[derive(Serialize, Deserialize, Debug)]
656pub struct CidrOCidr {
657    pub v4prefix: Option<Ipv4Addr>,
658    pub v6prefix: Option<Ipv6Addr>,
659    pub length: u8,
660}
661
662#[derive(Serialize, Deserialize, Debug)]
663#[serde(rename_all = "camelCase")]
664pub struct IpNetwork {
665    pub handle: String,
666    pub start_address: IpAddr,
667    pub end_address: IpAddr,
668    pub ip_version: IpVersion,
669    pub name: Option<String>,
670    pub country: Option<CountryCode>,
671    pub parent_handle: Option<String>,
672    #[serde(rename = "type")]
673    pub typ: Option<String>,
674    pub entities: Option<Vec<Entity>>,
675    pub links: Option<Vec<Link>>,
676    pub remarks: Option<NoticesOrRemarks>,
677    pub events: Option<Events>,
678    pub rdap_conformance: Option<Vec<String>>,
679    pub notices: Option<NoticesOrRemarks>,
680    pub port43: Option<String>,
681    pub status: Option<Vec<Status>>,
682    pub lang: Option<String>,
683    pub object_class_name: String,
684    // cidr0 extension
685    #[serde(rename = "cidr0_cidrs")]
686    pub cidr0_cidrs: Option<Vec<CidrOCidr>>,
687    /// From 'arin_originas0' extension.
688    #[serde(rename = "arin_originas0_originautnums")]
689    pub arin_originas0_originautnums: Option<Vec<u32>>,
690}
691
692/// https://tools.ietf.org/html/rfc7483#section-5.5
693#[derive(Serialize, Deserialize, Debug)]
694#[serde(rename_all = "camelCase")]
695pub struct AutNum {
696    pub handle: String,
697    pub start_autnum: Option<u32>,
698    pub end_autnum: Option<u32>,
699    pub name: Option<String>,
700    pub country: Option<CountryCode>,
701    #[serde(rename = "type")]
702    pub typ: Option<String>,
703    pub entities: Vec<Entity>,
704    pub links: Option<Vec<Link>>,
705    pub remarks: Option<NoticesOrRemarks>,
706    pub events: Option<Events>,
707    pub rdap_conformance: Option<Vec<String>>,
708    pub notices: Option<NoticesOrRemarks>,
709    pub port43: Option<String>,
710    pub status: Option<Vec<Status>>,
711    pub lang: Option<String>,
712    pub object_class_name: String,
713}
714
715/// https://tools.ietf.org/html/rfc7483#section-10.2.5
716#[derive(Serialize, Deserialize, Debug)]
717#[serde(rename_all = "lowercase")]
718enum DomainVariantRelation {
719    Registered,
720    Unregistered,
721    #[serde(rename = "registration restricted")]
722    RegistrationRestricted,
723    #[serde(rename = "open registration")]
724    OpenRegistration,
725    Conjoined,
726}
727
728#[derive(Serialize, Deserialize, Debug)]
729#[serde(rename_all = "camelCase")]
730pub struct VariantName {
731    ldh_name: String,
732    unicode_name: String,
733}
734
735#[derive(Serialize, Deserialize, Debug)]
736#[serde(rename_all = "camelCase")]
737pub struct Variant {
738    relation: Vec<DomainVariantRelation>,
739    idn_table: Option<String>,
740    variant_names: Vec<VariantName>,
741}
742
743/// For field sizes see https://tools.ietf.org/html/rfc4034#section-5.1
744#[derive(Serialize, Deserialize, Debug)]
745#[serde(rename_all = "camelCase")]
746pub struct DsData {
747    key_tag: Option<u16>,
748    algorithm: u8,
749    digest: String,
750    digest_type: u8,
751    events: Option<Events>,
752    links: Option<Vec<Link>>,
753}
754
755/// For field sizes see https://tools.ietf.org/html/rfc4034#section-2.1
756#[derive(Serialize, Deserialize, Debug)]
757#[serde(rename_all = "camelCase")]
758pub struct KeyData {
759    flags: u16,
760    protocol: u8,
761    public_key: String,
762    algorithm: u8,
763    events: Option<Events>,
764    links: Option<Vec<Link>>,
765}
766
767#[derive(Serialize, Deserialize, Debug)]
768#[serde(rename_all = "camelCase")]
769pub struct SecureDns {
770    zone_signed: Option<bool>,
771    delegation_signed: Option<bool>,
772    max_sig_life: Option<u32>,
773    ds_data: Option<Vec<DsData>>,
774    key_data: Option<Vec<KeyData>>,
775}
776
777/// https://fred.nic.cz/rdap-extension/
778#[derive(Serialize, Deserialize, Debug)]
779#[serde(rename_all = "camelCase")]
780pub struct FredKeyset {
781    pub links: Vec<Link>,
782    pub handle: String,
783    pub object_class_name: String,
784    #[serde(rename = "dns_keys")]
785    pub dns_keys: Vec<KeyData>,
786}
787
788/// https://fred.nic.cz/rdap-extension/
789#[derive(Serialize, Deserialize, Debug)]
790#[serde(rename_all = "camelCase")]
791pub struct FredNsset {
792    pub links: Vec<Link>,
793    pub handle: String,
794    pub object_class_name: String,
795    pub nameservers: Vec<Nameserver>,
796}
797
798/// https://tools.ietf.org/html/rfc7483#section-5.3
799#[derive(Serialize, Deserialize, Debug)]
800#[serde(rename_all = "camelCase")]
801pub struct Domain {
802    pub handle: Option<String>,
803    pub entities: Vec<Entity>,
804    pub links: Option<Vec<Link>>,
805    pub variants: Option<Vec<Variant>>,
806    pub nameservers: Option<Vec<Nameserver>>,
807    #[serde(rename = "secureDNS")]
808    pub secure_dns: Option<SecureDns>,
809    pub remarks: Option<NoticesOrRemarks>,
810    pub events: Events,
811    pub network: Option<IpNetwork>,
812    pub rdap_conformance: Option<Vec<String>>,
813    pub notices: Option<NoticesOrRemarks>,
814    pub port43: Option<String>,
815    pub status: Option<Vec<String>>,
816    pub lang: Option<String>,
817    pub object_class_name: String,
818    // fred extension
819    #[serde(rename = "fred_keyset")]
820    pub fred_keyset: Option<FredKeyset>,
821    #[serde(rename = "fred_nsset")]
822    pub fred_nsset: Option<FredNsset>,
823}
824
825/// https://tools.ietf.org/html/rfc7483.html#section-7
826#[derive(Serialize, Deserialize, Debug)]
827#[serde(rename_all = "camelCase")]
828pub struct Help {
829    rdap_conformance: Option<Vec<String>>,
830    notices: Option<NoticesOrRemarks>,
831}
832
833// https://tools.ietf.org/html/rfc7483#section-8
834#[derive(Serialize, Deserialize, Debug)]
835#[serde(rename_all = "camelCase")]
836pub struct EntitySearchResults {
837    rdap_conformance: Option<Vec<String>>,
838    notices: Option<NoticesOrRemarks>,
839    #[serde(rename = "entitySearchResults")]
840    results: Vec<Entity>,
841}
842
843#[derive(Serialize, Deserialize, Debug)]
844#[serde(rename_all = "camelCase")]
845pub struct DomainSearchResults {
846    rdap_conformance: Option<Vec<String>>,
847    notices: Option<NoticesOrRemarks>,
848    #[serde(rename = "domainSearchResults")]
849    results: Vec<Entity>,
850}
851
852#[derive(Serialize, Deserialize, Debug)]
853#[serde(rename_all = "camelCase")]
854pub struct NameserverSearchResults {
855    rdap_conformance: Option<Vec<String>>,
856    notices: Option<NoticesOrRemarks>,
857    #[serde(rename = "nameserverSearchResults")]
858    results: Vec<Entity>,
859}
860
861#[derive(Serialize, Deserialize, Debug)]
862#[serde(rename_all = "camelCase")]
863pub struct ArinOriginas0OriginautnumsResults {
864    rdap_conformance: Option<Vec<String>>,
865    notices: Option<NoticesOrRemarks>,
866    #[serde(rename = "arin_originas0_networkSearchResults")]
867    results: Vec<IpNetwork>,
868}
869
870// Some servers returns error code as string, so this function can deserialize
871// both number in string form and unsigned integer.
872fn deserialize_error_code<'de, D>(deserializer: D) -> Result<u16, D::Error>
873where
874    D: Deserializer<'de>,
875{
876    struct ErrorCodeVisitor;
877
878    impl<'de> Visitor<'de> for ErrorCodeVisitor {
879        type Value = u16;
880
881        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
882            write!(formatter, "expecting an error code as string or number")
883        }
884
885        fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
886        where
887            E: serde::de::Error,
888        {
889            u16::try_from(v).map_err(|_| {
890                serde::de::Error::invalid_value(Unexpected::Unsigned(v), &"an error code")
891            })
892        }
893
894        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
895        where
896            E: serde::de::Error,
897        {
898            u16::from_str(v)
899                .map_err(|_| serde::de::Error::invalid_value(Unexpected::Str(v), &"an error code"))
900        }
901    }
902
903    deserializer.deserialize_any(ErrorCodeVisitor)
904}
905
906/// https://tools.ietf.org/html/rfc7483#section-6
907#[derive(Serialize, Deserialize, Debug)]
908#[serde(rename_all = "camelCase")]
909pub struct Error {
910    #[serde(deserialize_with = "deserialize_error_code")]
911    error_code: u16,
912    title: String,
913    description: Option<Vec<String>>,
914    rdap_conformance: Option<Vec<String>>,
915    notices: Option<NoticesOrRemarks>,
916    lang: Option<String>,
917}
918
919pub trait BootstrapService {
920    fn keys(&self) -> &Vec<String>;
921    fn servers(&self) -> &Vec<String>;
922}
923
924#[derive(Deserialize, Debug)]
925pub struct BootstrapServiceRfc7484(Vec<String>, Vec<String>);
926
927impl BootstrapService for BootstrapServiceRfc7484 {
928    fn keys(&self) -> &Vec<String> {
929        &self.0
930    }
931    fn servers(&self) -> &Vec<String> {
932        &self.1
933    }
934}
935
936#[derive(Deserialize, Debug)]
937pub struct BootstrapServiceRfc8521(Vec<String>, Vec<String>, Vec<String>);
938
939impl BootstrapService for BootstrapServiceRfc8521 {
940    fn keys(&self) -> &Vec<String> {
941        &self.1
942    }
943    fn servers(&self) -> &Vec<String> {
944        &self.2
945    }
946}
947
948#[derive(Deserialize, Debug)]
949pub struct Bootstrap<T> {
950    pub description: Option<String>,
951    pub publication: DateTime<FixedOffset>,
952    pub services: Vec<T>,
953    pub version: String,
954}
955
956/// Bootstrap response that follows definition from RFC 7484.
957pub type BootstrapRfc7484 = Bootstrap<BootstrapServiceRfc7484>;
958/// Bootstrap response that follows definition from RFC 8521 (object tags).
959pub type BootstrapRfc8521 = Bootstrap<BootstrapServiceRfc8521>;
960
961#[cfg(test)]
962mod tests {
963    use super::*;
964    use serde::de::DeserializeOwned;
965    use std::fs::File;
966
967    #[test]
968    fn test_county_code() {
969        let country_code = CountryCode::from_str("CZ").unwrap();
970        assert_eq!(country_code.to_string(), "CZ");
971        assert_eq!(format!("{:?}", country_code), "CZ");
972        assert_eq!(country_code, CountryCode::from_str("cz").unwrap());
973
974        assert!(CountryCode::from_str("CZE").is_err());
975        assert!(CountryCode::from_str("C").is_err());
976        assert!(CountryCode::from_str("ČZ").is_err());
977    }
978
979    #[test]
980    fn test_country_code_serialize_deserialize() {
981        let item: CountryCode = serde_json::from_str(&"\"CZ\"").unwrap();
982        assert_eq!(item, CountryCode::from_str("CZ").unwrap());
983
984        let json = serde_json::to_string(&item).unwrap();
985        assert_eq!(json, "\"CZ\"");
986    }
987
988    #[test]
989    fn test_normalize_enum() {
990        let item: JCardItemDataType = serde_json::from_str(&"\"uri\"").unwrap();
991        assert_eq!(item, JCardItemDataType::Uri);
992
993        let item: JCardItemDataType = serde_json::from_str(&"\"URI\"").unwrap();
994        assert_eq!(item, JCardItemDataType::Uri);
995
996        let json = serde_json::to_string(&JCardItemDataType::Uri).unwrap();
997        assert_eq!(json, "\"uri\"");
998    }
999
1000    #[test]
1001    fn parse_vcard_multiple_values() {
1002        let json = r#"["vcard",[["version",{},"text","4.0"],["fn",{},"text",""],["adr",{"cc":"US","iso-3166-1-alpha-2":"US"},"text","","","","","Washington","",""],["org",{},"text","Amazon Registry Services, Inc."]]]"#;
1003        let jcard: JCard = serde_json::from_str(&json).unwrap();
1004        assert_eq!(jcard.typ(), JCardType::Vcard);
1005        assert_eq!(jcard.items().len(), 4);
1006
1007        assert_eq!(jcard.items_by_name("adr")[0].values.len(), 7);
1008        assert_eq!(
1009            jcard.items_by_name("org")[0].values[0],
1010            "Amazon Registry Services, Inc."
1011        );
1012
1013        let ser_json = serde_json::to_string(&jcard).unwrap();
1014        assert_eq!(json, ser_json);
1015    }
1016
1017    #[test]
1018    fn test_event_date_normal_format() {
1019        let json = r#"{"eventDate":"1990-12-31T23:59:59Z","eventAction":"last changed"}"#;
1020        let item: Event = serde_json::from_str(&json).unwrap();
1021        assert_eq!(item.event_date.to_rfc3339(), "1990-12-31T23:59:59+00:00");
1022    }
1023
1024    #[test]
1025    fn test_event_date_normal_format_with_timezone() {
1026        let json = r#"{"eventDate":"2011-07-05T12:48:24-04:00","eventAction":"last changed"}"#;
1027        let item: Event = serde_json::from_str(&json).unwrap();
1028        assert_eq!(item.event_date.to_rfc3339(), "2011-07-05T12:48:24-04:00");
1029    }
1030
1031    #[test]
1032    fn test_event_date_weird_format() {
1033        let json = r#"{"eventDate":"2019-09-20T11:45:06","eventAction":"last changed"}"#;
1034        let item: Event = serde_json::from_str(&json).unwrap();
1035        assert_eq!(item.event_date.to_rfc3339(), "2019-09-20T11:45:06+00:00");
1036    }
1037
1038    // xn--rhqv96g domain registry format
1039    #[test]
1040    fn test_event_date_weird_format_vol2() {
1041        let json = r#"{"eventAction":"last changed","eventDate":"2016-04-13 08:18:43"}"#;
1042        let item: Event = serde_json::from_str(&json).unwrap();
1043        assert_eq!(item.event_date.to_rfc3339(), "2016-04-13T08:18:43+00:00");
1044    }
1045
1046    // `mtr` domain registry format
1047    #[test]
1048    fn test_event_date_weird_format_vol3() {
1049        let json = r#"{"eventAction":"last changed","eventDate":"2015-08-25T00:00:00Z+0800"}"#;
1050        let item: Event = serde_json::from_str(&json).unwrap();
1051        assert_eq!(item.event_date.to_rfc3339(), "2015-08-25T00:00:00+08:00");
1052    }
1053
1054    #[test]
1055    fn test_notices_or_remarks() {
1056        let notices_or_remarks = NoticesOrRemarks(vec![NoticeOrRemark {
1057            title: Some("Title".into()),
1058            typ: None,
1059            description: Some(vec!["Ahoj".into()]),
1060            links: None,
1061        }]);
1062        assert_eq!(
1063            notices_or_remarks.description_by_title("title").unwrap()[0],
1064            "Ahoj"
1065        );
1066        assert!(notices_or_remarks.description_by_title("nothing").is_none());
1067    }
1068
1069    fn deserialize<T: DeserializeOwned>(path: &str) -> T {
1070        let file = File::open(format!("test_data/{}", path)).unwrap();
1071        let parsed: T = serde_json::from_reader(file).unwrap();
1072        parsed
1073    }
1074
1075    fn deserialize_and_serialize<T: DeserializeOwned + Serialize>(path: &str) -> T {
1076        let parsed = deserialize(path);
1077        // And convert back to JSON
1078        serde_json::to_string(&parsed).unwrap();
1079        parsed
1080    }
1081
1082    #[test]
1083    fn test_parse_entity_15() {
1084        let parsed: Entity = deserialize_and_serialize("entity/entity_15.json");
1085        assert_eq!("XXXX", parsed.handle.as_ref().unwrap());
1086        assert_eq!(1, parsed.as_event_actor.as_ref().unwrap().0.len());
1087    }
1088
1089    #[test]
1090    fn test_parse_entity_17() {
1091        let parsed: Entity = deserialize_and_serialize("entity/entity_17.json");
1092        assert_eq!("XXXX", parsed.handle.as_ref().unwrap());
1093    }
1094
1095    #[test]
1096    fn test_parse_entity_fred() {
1097        let parsed: Entity = deserialize_and_serialize("entity/entity_fred.json");
1098        assert_eq!("CZ-NIC", parsed.handle.as_ref().unwrap());
1099    }
1100
1101    #[test]
1102    fn test_parse_entity_ripe() {
1103        let parsed: Entity = deserialize_and_serialize("entity/entity_ripe.json");
1104        assert_eq!("ORG-RIEN1-RIPE", parsed.handle.as_ref().unwrap());
1105    }
1106
1107    #[test]
1108    fn test_parse_nameserver_18() {
1109        let parsed: Nameserver = deserialize_and_serialize("nameserver/nameserver_18.json");
1110        assert_eq!("XXXX", parsed.handle.unwrap());
1111    }
1112
1113    #[test]
1114    fn test_parse_nameserver_19() {
1115        let parsed: Nameserver = deserialize_and_serialize("nameserver/nameserver_19.json");
1116        assert_eq!("ns1.example.com", parsed.ldh_name);
1117    }
1118
1119    #[test]
1120    fn test_parse_nameserver_20() {
1121        let parsed: Nameserver = deserialize_and_serialize("nameserver/nameserver_20.json");
1122        assert_eq!("ns1.example.com", parsed.ldh_name);
1123    }
1124
1125    #[test]
1126    fn test_parse_nameserver_fred() {
1127        let parsed: Nameserver = deserialize_and_serialize("nameserver/nameserver_fred.json");
1128        assert_eq!("a.ns.nic.cz", parsed.ldh_name);
1129    }
1130
1131    #[test]
1132    fn test_parse_domain_23() {
1133        let parsed: Domain = deserialize_and_serialize("domain/domain_23.json");
1134        assert_eq!("XXXX", parsed.handle.unwrap());
1135    }
1136
1137    #[test]
1138    fn test_parse_domain_24() {
1139        let parsed: Domain = deserialize_and_serialize("domain/domain_24.json");
1140        assert_eq!("XXXX", parsed.handle.unwrap());
1141    }
1142
1143    #[test]
1144    fn test_parse_domain_fred() {
1145        let parsed: Domain = deserialize_and_serialize("domain/domain_fred.json");
1146        assert_eq!("nic.cz", parsed.handle.unwrap());
1147    }
1148
1149    #[test]
1150    fn test_parse_domain_ripe_reverse() {
1151        let parsed: Domain = deserialize_and_serialize("domain/domain_ripe_reverse.json");
1152        assert_eq!("6.0.193.in-addr.arpa", parsed.handle.unwrap());
1153    }
1154
1155    #[test]
1156    fn test_parse_ip_network_26() {
1157        let parsed: IpNetwork = deserialize_and_serialize("ip_network/ip_network_26.json");
1158        assert_eq!("XXXX-RIR", parsed.handle);
1159    }
1160
1161    #[test]
1162    fn test_parse_ip_network_apnic_1_1_1_1() {
1163        let parsed: IpNetwork =
1164            deserialize_and_serialize("ip_network/ip_network_apnic_1_1_1_1.json");
1165        assert_eq!("1.1.1.0 - 1.1.1.255", parsed.handle);
1166    }
1167
1168    #[test]
1169    fn test_parse_ip_network_arin_3_3_3_3() {
1170        let parsed: IpNetwork =
1171            deserialize_and_serialize("ip_network/ip_network_arin_3_3_3_3.json");
1172        assert_eq!("NET-3-0-0-0-1", parsed.handle);
1173    }
1174
1175    #[test]
1176    fn test_parse_ip_network_ripe_193_0_0_0() {
1177        let parsed: IpNetwork =
1178            deserialize_and_serialize("ip_network/ip_network_ripe_193_0_0_0.json");
1179        assert_eq!("193.0.0.0 - 193.0.7.255", parsed.handle);
1180    }
1181
1182    #[test]
1183    fn test_parse_ip_network_afrinic() {
1184        let parsed: IpNetwork = deserialize_and_serialize("ip_network/ip_network_afrinic.json");
1185        assert_eq!("41.0.0.0 - 41.0.255.255", parsed.handle);
1186    }
1187
1188    #[test]
1189    fn test_parse_ip_network_br() {
1190        let parsed: IpNetwork = deserialize_and_serialize("ip_network/ip_network_br.json");
1191        assert_eq!("177.0.0.0/14", parsed.handle);
1192    }
1193
1194    #[test]
1195    fn test_parse_ip_network_lacnic() {
1196        let parsed: IpNetwork = deserialize_and_serialize("ip_network/ip_network_lacnic.json");
1197        assert_eq!("179.0.0.0/23", parsed.handle);
1198    }
1199
1200    #[test]
1201    fn test_parse_autnum_27() {
1202        let parsed: AutNum = deserialize_and_serialize("autnum/autnum_27.json");
1203        assert_eq!("XXXX-RIR", parsed.handle);
1204    }
1205
1206    #[test]
1207    fn test_parse_autnum_ripe_as1234() {
1208        let parsed: AutNum = deserialize_and_serialize("autnum/autnum_ripe_as1234.json");
1209        assert_eq!("AS1234", parsed.handle);
1210    }
1211
1212    #[test]
1213    fn test_parse_autnum_arin_as256() {
1214        let parsed: AutNum = deserialize_and_serialize("autnum/autnum_arin_as256.json");
1215        assert_eq!("AS256", parsed.handle);
1216    }
1217
1218    #[test]
1219    fn test_parse_autnum_afrinic_as36864() {
1220        let parsed: AutNum = deserialize_and_serialize("autnum/autnum_afrinic_as36864.json");
1221        assert_eq!("AS36864", parsed.handle);
1222    }
1223
1224    #[test]
1225    fn test_parse_autnum_apnic_as4608() {
1226        let parsed: AutNum = deserialize_and_serialize("autnum/autnum_apnic_as4608.json");
1227        assert_eq!("AS4608", parsed.handle);
1228    }
1229
1230    #[test]
1231    fn test_parse_autnum_lacnic_as27648() {
1232        let parsed: AutNum = deserialize_and_serialize("autnum/autnum_lacnic_as27648.json");
1233        assert_eq!("27648", parsed.handle);
1234    }
1235
1236    #[test]
1237    fn test_parse_arin_originas0_network_search_results() {
1238        let parsed: ArinOriginas0OriginautnumsResults =
1239            deserialize_and_serialize("arin_originas0_networkSearchResults.json");
1240        assert!(parsed.results.len() > 0);
1241    }
1242
1243    #[test]
1244    fn test_parse_error_28() {
1245        let parsed: Error = deserialize_and_serialize("error/error_28.json");
1246        assert_eq!(418, parsed.error_code);
1247    }
1248
1249    #[test]
1250    fn test_parse_error_29() {
1251        let parsed: Error = deserialize_and_serialize("error/error_29.json");
1252        assert_eq!(418, parsed.error_code);
1253    }
1254
1255    #[test]
1256    fn test_parse_error_apnic_400() {
1257        let parsed: Error = deserialize_and_serialize("error/error_apnic_400.json");
1258        assert_eq!(400, parsed.error_code);
1259    }
1260
1261    #[test]
1262    fn test_parse_error_ripe_404() {
1263        let parsed: Error = deserialize_and_serialize("error/error_ripe_404.json");
1264        assert_eq!(404, parsed.error_code);
1265    }
1266
1267    #[test]
1268    fn test_parse_bootstrap_asn() {
1269        let parsed: BootstrapRfc7484 = deserialize("bootstrap/asn.json");
1270        assert!(parsed.services.len() > 0);
1271    }
1272
1273    #[test]
1274    fn test_parse_bootstrap_dns() {
1275        let parsed: BootstrapRfc7484 = deserialize("bootstrap/dns.json");
1276        assert!(parsed.services.len() > 0);
1277    }
1278
1279    #[test]
1280    fn test_parse_bootstrap_ipv4() {
1281        let parsed: BootstrapRfc7484 = deserialize("bootstrap/ipv4.json");
1282        assert!(parsed.services.len() > 0);
1283    }
1284
1285    #[test]
1286    fn test_parse_bootstrap_ipv6() {
1287        let parsed: BootstrapRfc7484 = deserialize("bootstrap/ipv6.json");
1288        assert!(parsed.services.len() > 0);
1289    }
1290
1291    #[test]
1292    fn test_parse_bootstrap_object_tags() {
1293        let parsed: BootstrapRfc8521 = deserialize("bootstrap/object-tags.json");
1294        assert!(parsed.services.len() > 0);
1295    }
1296}