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}