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}