trust_dns_proto/rr/rdata/
srv.rs

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