hickory_proto/rr/rdata/
srv.rs

1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! service records for identify port mapping for specific services on a host
9use std::fmt;
10
11#[cfg(feature = "serde-config")]
12use serde::{Deserialize, Serialize};
13
14use crate::{
15    error::ProtoResult,
16    rr::{domain::Name, RData, RecordData, RecordType},
17    serialize::binary::{BinDecodable, BinDecoder, BinEncodable, BinEncoder},
18};
19
20/// [RFC 2782, DNS SRV RR, February 2000](https://tools.ietf.org/html/rfc2782)
21///
22/// ```text
23/// Introductory example
24///
25///  If a SRV-cognizant LDAP client wants to discover a LDAP server that
26///  supports TCP protocol and provides LDAP service for the domain
27///  example.com., it does a lookup of
28///
29/// _ldap._tcp.example.com
30///
31///  as described in [ARM].  The example zone file near the end of this
32///  memo contains answering RRs for an SRV query.
33///
34///  Note: LDAP is chosen as an example for illustrative purposes only,
35///  and the LDAP examples used in this document should not be considered
36///  a definitive statement on the recommended way for LDAP to use SRV
37///  records. As described in the earlier applicability section, consult
38///  the appropriate LDAP documents for the recommended procedures.
39///
40/// The format of the SRV RR
41///
42///  Here is the format of the SRV RR, whose DNS type code is 33:
43///
44/// _Service._Proto.Name TTL Class SRV Priority Weight Port Target
45///
46/// (There is an example near the end of this document.)
47///
48///  Service
49/// The symbolic name of the desired service, as defined in Assigned
50/// Numbers [STD 2] or locally.  An underscore (_) is prepended to
51/// the service identifier to avoid collisions with DNS labels that
52/// occur in nature.
53///
54/// Some widely used services, notably POP, don't have a single
55/// universal name.  If Assigned Numbers names the service
56/// indicated, that name is the only name which is legal for SRV
57/// lookups.  The Service is case insensitive.
58///
59///  Proto
60/// The symbolic name of the desired protocol, with an underscore
61/// (_) prepended to prevent collisions with DNS labels that occur
62/// in nature.  _TCP and _UDP are at present the most useful values
63/// for this field, though any name defined by Assigned Numbers or
64/// locally may be used (as for Service).  The Proto is case
65/// insensitive.
66///
67///  Name
68/// The domain this RR refers to.  The SRV RR is unique in that the
69/// name one searches for is not this name; the example near the end
70/// shows this clearly.
71///
72///  TTL
73/// Standard DNS meaning [RFC 1035].
74///
75///  Class
76/// Standard DNS meaning [RFC 1035].   SRV records occur in the IN
77/// Class.
78///
79/// ```
80#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
81#[derive(Debug, PartialEq, Eq, Hash, Clone)]
82pub struct SRV {
83    priority: u16,
84    weight: u16,
85    port: u16,
86    target: Name,
87}
88
89impl SRV {
90    /// Creates a new SRV record data.
91    ///
92    /// # Arguments
93    ///
94    /// * `priority` - lower values have a higher priority and clients will attempt to use these
95    ///                first.
96    /// * `weight` - for servers with the same priority, higher weights will be chosen more often.
97    /// * `port` - the socket port number on which the service is listening.
98    /// * `target` - like CNAME, this is the target domain name to which the service is associated.
99    ///
100    /// # Return value
101    ///
102    /// The newly constructed SRV record data.
103    pub fn new(priority: u16, weight: u16, port: u16, target: Name) -> Self {
104        Self {
105            priority,
106            weight,
107            port,
108            target,
109        }
110    }
111
112    /// ```text
113    ///  Priority
114    /// The priority of this target host.  A client MUST attempt to
115    /// contact the target host with the lowest-numbered priority it can
116    /// reach; target hosts with the same priority SHOULD be tried in an
117    /// order defined by the weight field.  The range is 0-65535.  This
118    /// is a 16 bit unsigned integer in network byte order.
119    /// ```
120    pub fn priority(&self) -> u16 {
121        self.priority
122    }
123
124    /// ```text
125    ///  Weight
126    /// A server selection mechanism.  The weight field specifies a
127    /// relative weight for entries with the same priority. Larger
128    /// weights SHOULD be given a proportionately higher probability of
129    /// being selected. The range of this number is 0-65535.  This is a
130    /// 16 bit unsigned integer in network byte order.  Domain
131    /// administrators SHOULD use Weight 0 when there isn't any server
132    /// selection to do, to make the RR easier to read for humans (less
133    /// noisy).  In the presence of records containing weights greater
134    /// than 0, records with weight 0 should have a very small chance of
135    /// being selected.
136    ///
137    /// In the absence of a protocol whose specification calls for the
138    /// use of other weighting information, a client arranges the SRV
139    /// RRs of the same Priority in the order in which target hosts,
140    /// specified by the SRV RRs, will be contacted. The following
141    /// algorithm SHOULD be used to order the SRV RRs of the same
142    /// priority:
143    ///
144    /// To select a target to be contacted next, arrange all SRV RRs
145    /// (that have not been ordered yet) in any order, except that all
146    /// those with weight 0 are placed at the beginning of the list.
147    ///
148    /// Compute the sum of the weights of those RRs, and with each RR
149    /// associate the running sum in the selected order. Then choose a
150    /// uniform random number between 0 and the sum computed
151    /// (inclusive), and select the RR whose running sum value is the
152    /// first in the selected order which is greater than or equal to
153    /// the random number selected. The target host specified in the
154    /// selected SRV RR is the next one to be contacted by the client.
155    /// Remove this SRV RR from the set of the unordered SRV RRs and
156    /// apply the described algorithm to the unordered SRV RRs to select
157    /// the next target host.  Continue the ordering process until there
158    /// are no unordered SRV RRs.  This process is repeated for each
159    /// Priority.
160    /// ```
161    pub fn weight(&self) -> u16 {
162        self.weight
163    }
164
165    /// ```text
166    ///  Port
167    /// The port on this target host of this service.  The range is 0-
168    /// 65535.  This is a 16 bit unsigned integer in network byte order.
169    /// This is often as specified in Assigned Numbers but need not be.
170    ///
171    /// ```
172    pub fn port(&self) -> u16 {
173        self.port
174    }
175
176    /// ```text
177    ///  Target
178    /// The domain name of the target host.  There MUST be one or more
179    /// address records for this name, the name MUST NOT be an alias (in
180    /// the sense of RFC 1034 or RFC 2181).  Implementors are urged, but
181    /// not required, to return the address record(s) in the Additional
182    /// Data section.  Unless and until permitted by future standards
183    /// action, name compression is not to be used for this field.
184    ///
185    /// A Target of "." means that the service is decidedly not
186    /// available at this domain.
187    /// ```
188    pub fn target(&self) -> &Name {
189        &self.target
190    }
191}
192
193impl BinEncodable for SRV {
194    /// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-6), DNSSEC Resource Records, March 2005
195    ///
196    /// This is accurate for all currently known name records.
197    ///
198    /// ```text
199    /// 6.2.  Canonical RR Form
200    ///
201    ///    For the purposes of DNS security, the canonical form of an RR is the
202    ///    wire format of the RR where:
203    ///
204    ///    ...
205    ///
206    ///    3.  if the type of the RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
207    ///        HINFO, MINFO, MX, HINFO, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
208    ///        SRV, DNAME, A6, RRSIG, or (rfc6840 removes NSEC), all uppercase
209    ///        US-ASCII letters in the DNS names contained within the RDATA are replaced
210    ///        by the corresponding lowercase US-ASCII letters;
211    /// ```
212    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
213        let is_canonical_names = encoder.is_canonical_names();
214
215        encoder.emit_u16(self.priority())?;
216        encoder.emit_u16(self.weight())?;
217        encoder.emit_u16(self.port())?;
218
219        // to_lowercase for rfc4034 and rfc6840
220        self.target()
221            .emit_with_lowercase(encoder, is_canonical_names)?;
222        Ok(())
223    }
224}
225
226impl<'r> BinDecodable<'r> for SRV {
227    fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
228        // SRV { priority: u16, weight: u16, port: u16, target: Name, },
229        Ok(Self::new(
230            decoder.read_u16()?.unverified(/*any u16 is valid*/),
231            decoder.read_u16()?.unverified(/*any u16 is valid*/),
232            decoder.read_u16()?.unverified(/*any u16 is valid*/),
233            Name::read(decoder)?,
234        ))
235    }
236}
237
238impl RecordData for SRV {
239    fn try_from_rdata(data: RData) -> Result<Self, RData> {
240        match data {
241            RData::SRV(data) => Ok(data),
242            _ => Err(data),
243        }
244    }
245
246    fn try_borrow(data: &RData) -> Option<&Self> {
247        match data {
248            RData::SRV(data) => Some(data),
249            _ => None,
250        }
251    }
252
253    fn record_type(&self) -> RecordType {
254        RecordType::SRV
255    }
256
257    fn into_rdata(self) -> RData {
258        RData::SRV(self)
259    }
260}
261
262/// [RFC 2782, DNS SRV RR, February 2000](https://tools.ietf.org/html/rfc2782)
263///
264/// ```text
265/// The format of the SRV RR
266///
267///   Here is the format of the SRV RR, whose DNS type code is 33:
268///
269///   _Service._Proto.Name TTL Class SRV Priority Weight Port Target
270///
271///   (There is an example near the end of this document.)
272/// ```
273impl fmt::Display for SRV {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
275        write!(
276            f,
277            "{priority} {weight} {port} {target}",
278            priority = self.priority,
279            weight = self.weight,
280            port = self.port,
281            target = self.target,
282        )
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    #![allow(clippy::dbg_macro, clippy::print_stdout)]
289
290    use super::*;
291
292    #[test]
293    fn test() {
294        use std::str::FromStr;
295
296        let rdata = SRV::new(1, 2, 3, Name::from_str("_dns._tcp.example.com").unwrap());
297
298        let mut bytes = Vec::new();
299        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
300        assert!(rdata.emit(&mut encoder).is_ok());
301        let bytes = encoder.into_bytes();
302
303        println!("bytes: {bytes:?}");
304
305        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
306
307        let read_rdata = SRV::read(&mut decoder).expect("Decoding error");
308        assert_eq!(rdata, read_rdata);
309    }
310}