annotate src/resolv_addr.rs @ 4:66c0e10c89fc

Support resolving hostnames Until now the crate supported only IP addresses and systemd sockets. This was troublesome because it prevented the popular `localhost:1234` format. This commit changes the behavior so that if parsing of `std::net::SocketAddr` fails it attempts to parse it as `hostname:port`. `bind_*()` methods were also modified to be async because of this.
author Martin Habovstiak <martin.habovstiak@gmail.com>
date Fri, 27 Nov 2020 15:05:19 +0100
parents
children cfef4593e207
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
1 use thiserror::Error;
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
2 use std::fmt;
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
3
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
4 #[derive(Debug, PartialEq)]
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
5 pub(crate) struct ResolvAddr(String);
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
6
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
7 impl ResolvAddr {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
8 pub(crate) fn as_str(&self) -> &str {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
9 &self.0
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
10 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
11
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
12 pub(crate) fn try_from_generic<T: std::ops::Deref<Target=str> + Into<String>>(string: T) -> Result<Self, ResolvAddrError> {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
13 // can't use a combinator due to borrowing
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
14 let colon = match string.rfind(':') {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
15 Some(colon) => colon,
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
16 None => return Err(ResolvAddrError::MissingPort(string.into())),
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
17 };
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
18
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
19 let (hostname, port) = string.split_at(colon);
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
20
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
21 if let Err(error) = port[1..].parse::<u16>() {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
22 return Err(ResolvAddrError::InvalidPort { string: string.into(), error, });
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
23 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
24
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
25 let len = hostname.len();
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
26 if len > 253 {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
27 return Err(ResolvAddrError::TooLong { string: string.into(), len, } )
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
28 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
29
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
30 let mut label_start = 0usize;
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
31
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
32 for (i, c) in hostname.chars().enumerate() {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
33 match c {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
34 '.' => {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
35 if i - label_start == 0 {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
36 return Err(ResolvAddrError::EmptyLabel { string: string.into(), label_start, });
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
37 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
38
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
39 label_start = i + 1;
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
40 },
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
41 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' => (),
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
42 _ => return Err(ResolvAddrError::InvalidCharacter { string: string.into(), c, pos: i, }),
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
43 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
44
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
45 if i - label_start > 63 {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
46 return Err(ResolvAddrError::LongLabel { string: string.into(), label_start, label_end: i, });
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
47 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
48 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
49
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
50 Ok(ResolvAddr(string.into()))
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
51 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
52 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
53
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
54 impl fmt::Display for ResolvAddr {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
56 fmt::Display::fmt(&self.0, f)
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
57 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
58 }
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
59
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
60
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
61 #[derive(Debug, Error)]
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
62 pub(crate) enum ResolvAddrError {
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
63 #[error("hostname {string} has {len} character which exceeds the limit of 253")]
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
64 TooLong { string: String, len: usize },
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
65 #[error("invalid character {c} in hostname {string} at position {pos}")]
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
66 InvalidCharacter { string: String, pos: usize, c: char, },
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
67 #[error("hostname {string} contains a label {} at position {label_start} which is {} characters long - more than the limit 63", &string[(*label_start)..(*label_end)], label_end - label_start)]
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
68 LongLabel { string: String, label_start: usize, label_end: usize, },
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
69 #[error("hostname {string} contains an empty label at position {label_start}")]
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
70 EmptyLabel { string: String, label_start: usize, },
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
71 #[error("the address {0} is missing a port")]
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
72 MissingPort(String),
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
73 #[error("failed to parse port numer in the address {string}")]
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
74 InvalidPort { string: String, error: std::num::ParseIntError, },
66c0e10c89fc Support resolving hostnames
Martin Habovstiak <martin.habovstiak@gmail.com>
parents:
diff changeset
75 }