1 //! General utility modules for use all over the code base
2 use std::{cmp::Ordering, fmt, net::IpAddr, str::FromStr};
3 
4 use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
5 
6 use bytes::Bytes;
7 use rpki::{
8     repository::crypto::DigestAlgorithm,
9     uri::{Https, Rsync},
10 };
11 
12 use crate::constants::KRILL_VERSION;
13 
14 #[cfg(feature = "hsm")]
15 pub mod dummysigner;
16 pub mod ext_serde;
17 pub mod file;
18 pub mod httpclient;
19 pub mod softsigner;
20 pub mod xml;
21 
22 //------------ KrillVersion --------------------------------------------------
23 
24 /// Defines a Krill version. Will
25 
26 #[derive(Clone, Debug, Eq, PartialEq)]
27 pub struct KrillVersion {
28     major: u64,
29     minor: u64,
30     patch: u64,
31     release_type: KrillVersionReleaseType,
32 }
33 
34 impl KrillVersion {
current() -> Self35     pub fn current() -> Self {
36         // Note: we have a unit test to ensure that the KRILL_VERSION constant
37         // which is derived from the Cargo.toml version can be parsed.
38         Self::from_str(KRILL_VERSION).unwrap()
39     }
40 
v0_5_0_or_before() -> Self41     pub fn v0_5_0_or_before() -> Self {
42         Self::dev(0, 5, 0, "or-before".to_string())
43     }
44 
release(major: u64, minor: u64, patch: u64) -> Self45     pub fn release(major: u64, minor: u64, patch: u64) -> Self {
46         KrillVersion {
47             major,
48             minor,
49             patch,
50             release_type: KrillVersionReleaseType::Release,
51         }
52     }
53 
candidate(major: u64, minor: u64, patch: u64, number: u64) -> Self54     pub fn candidate(major: u64, minor: u64, patch: u64, number: u64) -> Self {
55         KrillVersion {
56             major,
57             minor,
58             patch,
59             release_type: KrillVersionReleaseType::Candidate(number),
60         }
61     }
62 
dev(major: u64, minor: u64, patch: u64, addition: String) -> Self63     fn dev(major: u64, minor: u64, patch: u64, addition: String) -> Self {
64         KrillVersion {
65             major,
66             minor,
67             patch,
68             release_type: KrillVersionReleaseType::Dev(addition),
69         }
70     }
71 }
72 
73 impl FromStr for KrillVersion {
74     type Err = KrillVersionParseError;
75 
from_str(s: &str) -> Result<Self, Self::Err>76     fn from_str(s: &str) -> Result<Self, Self::Err> {
77         // x.y.z       => major.minor.patch release
78         // x.y.z-rc#   => major.minor.patch release candidate #
79         // x.y.z-<str> => major.minor.patch dev 'str'
80         // other       => cannot parse
81         //
82         // Support legacy enum based version notation as well:
83         // V0_6 => 0.6.0
84 
85         let parts: Vec<&str> = s.split('.').collect();
86         if parts.len() == 3 {
87             let major = u64::from_str(parts[0]).map_err(|_| KrillVersionParseError::for_str(s))?;
88 
89             let minor = u64::from_str(parts[1]).map_err(|_| KrillVersionParseError::for_str(s))?;
90 
91             let mut patch_parts = parts[2].split('-');
92 
93             let patch = u64::from_str(patch_parts.next().unwrap()).map_err(|_| KrillVersionParseError::for_str(s))?;
94 
95             match patch_parts.next() {
96                 None => Ok(KrillVersion::release(major, minor, patch)),
97                 Some(addition) => {
98                     if addition.len() > 2 && addition.starts_with("rc") {
99                         let number = u64::from_str(&addition[2..]).map_err(|_| KrillVersionParseError::for_str(s))?;
100                         Ok(KrillVersion::candidate(major, minor, patch, number))
101                     } else {
102                         Ok(KrillVersion::dev(major, minor, patch, addition.to_string()))
103                     }
104                 }
105             }
106         } else {
107             match s {
108                 // Enums present in versions before 0.9.1
109                 "V0_6" => Ok(KrillVersion::release(0, 6, 0)),
110                 "V0_7" => Ok(KrillVersion::release(0, 7, 0)),
111                 "V0_8_0_RC1" => Ok(KrillVersion::candidate(0, 8, 0, 1)),
112                 "V0_8" => Ok(KrillVersion::release(0, 8, 0)),
113                 "V0_8_1_RC1" => Ok(KrillVersion::candidate(0, 8, 1, 1)),
114                 "V0_8_1" => Ok(KrillVersion::release(0, 8, 1)),
115                 "V0_8_2" => Ok(KrillVersion::release(0, 8, 2)),
116                 "V0_9_0_RC1" => Ok(KrillVersion::candidate(0, 9, 0, 1)),
117                 "V0_9_0" => Ok(KrillVersion::release(0, 9, 0)),
118                 _ => Err(KrillVersionParseError::for_str(s)),
119             }
120         }
121     }
122 }
123 
124 impl fmt::Display for KrillVersion {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result125     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126         write!(f, "{}.{}.{}{}", self.major, self.minor, self.patch, self.release_type)
127     }
128 }
129 
130 #[derive(Clone, Debug)]
131 pub struct KrillVersionParseError(String);
132 
133 impl KrillVersionParseError {
for_str(s: &str) -> Self134     fn for_str(s: &str) -> Self {
135         KrillVersionParseError(s.to_string())
136     }
137 }
138 
139 impl fmt::Display for KrillVersionParseError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result140     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141         write!(f, "Could not parse Krill version from string: {}", self.0)
142     }
143 }
144 
145 impl Ord for KrillVersion {
cmp(&self, other: &Self) -> Ordering146     fn cmp(&self, other: &Self) -> Ordering {
147         let mut res = self.major.cmp(&other.major);
148 
149         // use res.is_eq() when the minimum rust requirement will be 1.53 or higher
150         if res == Ordering::Equal {
151             res = self.minor.cmp(&other.minor);
152         }
153 
154         if res == Ordering::Equal {
155             res = self.patch.cmp(&other.patch);
156         }
157 
158         if res == Ordering::Equal {
159             res = self.release_type.cmp(&other.release_type);
160         }
161 
162         res
163     }
164 }
165 
166 impl PartialOrd for KrillVersion {
partial_cmp(&self, other: &Self) -> Option<Ordering>167     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
168         Some(self.cmp(other))
169     }
170 }
171 
172 impl Serialize for KrillVersion {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,173     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
174     where
175         S: Serializer,
176     {
177         self.to_string().serialize(serializer)
178     }
179 }
180 
181 impl<'de> Deserialize<'de> for KrillVersion {
deserialize<D>(deserializer: D) -> std::result::Result<KrillVersion, D::Error> where D: Deserializer<'de>,182     fn deserialize<D>(deserializer: D) -> std::result::Result<KrillVersion, D::Error>
183     where
184         D: Deserializer<'de>,
185     {
186         let string = String::deserialize(deserializer)?;
187         KrillVersion::from_str(string.as_str()).map_err(de::Error::custom)
188     }
189 }
190 
191 #[derive(Clone, Debug, Eq, PartialEq)]
192 enum KrillVersionReleaseType {
193     Release,
194     Candidate(u64),
195     Dev(String),
196 }
197 
198 impl Ord for KrillVersionReleaseType {
cmp(&self, other: &Self) -> Ordering199     fn cmp(&self, other: &Self) -> Ordering {
200         match &self {
201             KrillVersionReleaseType::Release => match other {
202                 KrillVersionReleaseType::Release => Ordering::Equal,
203                 _ => Ordering::Greater,
204             },
205             KrillVersionReleaseType::Candidate(nr) => match other {
206                 KrillVersionReleaseType::Release => Ordering::Less,
207                 KrillVersionReleaseType::Candidate(nr_other) => nr.cmp(nr_other),
208                 &KrillVersionReleaseType::Dev(_) => Ordering::Greater,
209             },
210             KrillVersionReleaseType::Dev(_) => match other {
211                 KrillVersionReleaseType::Dev(_) => Ordering::Equal,
212                 _ => Ordering::Less,
213             },
214         }
215     }
216 }
217 
218 impl PartialOrd for KrillVersionReleaseType {
partial_cmp(&self, other: &Self) -> Option<Ordering>219     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
220         Some(self.cmp(other))
221     }
222 }
223 
224 impl fmt::Display for KrillVersionReleaseType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result225     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226         match self {
227             KrillVersionReleaseType::Release => write!(f, ""),
228             KrillVersionReleaseType::Candidate(nr) => write!(f, "-rc{}", nr),
229             KrillVersionReleaseType::Dev(text) => write!(f, "-{}", text),
230         }
231     }
232 }
233 
234 /// Returns the SHA256 hash for the given octets.
sha256(object: &[u8]) -> Bytes235 pub fn sha256(object: &[u8]) -> Bytes {
236     let digest = DigestAlgorithm::default().digest(object);
237     Bytes::copy_from_slice(digest.as_ref())
238 }
239 
240 // TODO: check that an IP address is_global() when that stabilizes: https://github.com/rust-lang/rust/issues/27709
241 /// Assumes that non-ip hostnames are global (they may of course resolve to something that isn't but hey we tried to help)
seems_global_uri(auth: &str) -> bool242 fn seems_global_uri(auth: &str) -> bool {
243     if auth.to_lowercase() == "localhost" || auth.starts_with('[') || IpAddr::from_str(auth).is_ok() {
244         false
245     } else if let Some(i) = auth.rfind(':') {
246         let auth = &auth[0..i];
247         IpAddr::from_str(auth).is_err()
248     } else {
249         // appears to be a non-ip hostname, assume it's global
250         true
251     }
252 }
253 
254 pub trait AllowedUri {
authority(&self) -> &str255     fn authority(&self) -> &str;
256 
seems_global_uri(&self) -> bool257     fn seems_global_uri(&self) -> bool {
258         seems_global_uri(self.authority())
259     }
260 }
261 
262 impl AllowedUri for Rsync {
authority(&self) -> &str263     fn authority(&self) -> &str {
264         self.authority()
265     }
266 }
267 
268 impl AllowedUri for Https {
authority(&self) -> &str269     fn authority(&self) -> &str {
270         self.authority()
271     }
272 }
273 
274 //------------ Tests ---------------------------------------------------------
275 
276 #[cfg(test)]
277 mod tests {
278 
279     use crate::commons::util::seems_global_uri;
280 
281     use super::*;
282 
283     #[test]
check_uri_seems_global()284     fn check_uri_seems_global() {
285         // Does not seem global
286         assert!(!seems_global_uri("localhost"));
287         assert!(!seems_global_uri("0.0.0.0"));
288         assert!(!seems_global_uri("127.0.0.1"));
289         assert!(!seems_global_uri("127.0.0.1:873"));
290         assert!(!seems_global_uri("1.2.3.4"));
291         assert!(!seems_global_uri("::"));
292         assert!(!seems_global_uri("::1"));
293         assert!(!seems_global_uri("[::1]:873"));
294         assert!(!seems_global_uri("2001:0db8:85a3:0000:0000:8a2e:0370:7334"));
295 
296         // Looks ok
297         assert!(seems_global_uri("localghost"));
298         assert!(seems_global_uri("rpki.bla"));
299     }
300 
301     #[test]
krill_version_from_current_cargo_version()302     fn krill_version_from_current_cargo_version() {
303         KrillVersion::current();
304     }
305 
306     #[test]
krill_version_pre_0_9_1_enums()307     fn krill_version_pre_0_9_1_enums() {
308         KrillVersion::from_str("V0_6").unwrap();
309         KrillVersion::from_str("V0_7").unwrap();
310         KrillVersion::from_str("V0_8_0_RC1").unwrap();
311         KrillVersion::from_str("V0_8").unwrap();
312         KrillVersion::from_str("V0_8_1_RC1").unwrap();
313         KrillVersion::from_str("V0_8_1").unwrap();
314         KrillVersion::from_str("V0_8_2").unwrap();
315         KrillVersion::from_str("V0_9_0_RC1").unwrap();
316         KrillVersion::from_str("V0_9_0").unwrap();
317     }
318 
319     #[test]
krill_version_from_str()320     fn krill_version_from_str() {
321         KrillVersion::from_str("0.9.1").unwrap();
322         KrillVersion::from_str("0.9.1-rc1").unwrap();
323         KrillVersion::from_str("0.9.1-bis").unwrap();
324 
325         // We do not support short, or random notations including but not limited to:
326         assert!(KrillVersion::from_str("v0.9.1").is_err());
327         assert!(KrillVersion::from_str("0.9-bis").is_err());
328         assert!(KrillVersion::from_str("some garbage").is_err());
329     }
330 
331     #[test]
krill_version_ordering()332     fn krill_version_ordering() {
333         let v0_9_1 = KrillVersion::from_str("0.9.1").unwrap();
334         let v0_9_1_rc1 = KrillVersion::from_str("0.9.1-rc1").unwrap();
335         let v0_9_1_rc2 = KrillVersion::from_str("0.9.1-rc2").unwrap();
336         let v0_9_1_dev = KrillVersion::from_str("0.9.1-dev").unwrap();
337 
338         assert!(v0_9_1 > v0_9_1_rc1);
339         assert!(v0_9_1_rc2 > v0_9_1_rc1);
340         assert!(v0_9_1_rc1 > v0_9_1_dev);
341 
342         let v0_9_0 = KrillVersion::from_str("0.9.0").unwrap();
343         assert!(v0_9_1 > v0_9_0);
344         assert!(v0_9_1_rc2 > v0_9_0);
345         assert!(v0_9_1_dev > v0_9_0);
346     }
347 }
348