Mercurial > crates > systemd-socket
comparison 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 | 
   comparison
  equal
  deleted
  inserted
  replaced
| 3:0edcde404b02 | 4:66c0e10c89fc | 
|---|---|
| 1 use thiserror::Error; | |
| 2 use std::fmt; | |
| 3 | |
| 4 #[derive(Debug, PartialEq)] | |
| 5 pub(crate) struct ResolvAddr(String); | |
| 6 | |
| 7 impl ResolvAddr { | |
| 8 pub(crate) fn as_str(&self) -> &str { | |
| 9 &self.0 | |
| 10 } | |
| 11 | |
| 12 pub(crate) fn try_from_generic<T: std::ops::Deref<Target=str> + Into<String>>(string: T) -> Result<Self, ResolvAddrError> { | |
| 13 // can't use a combinator due to borrowing | |
| 14 let colon = match string.rfind(':') { | |
| 15 Some(colon) => colon, | |
| 16 None => return Err(ResolvAddrError::MissingPort(string.into())), | |
| 17 }; | |
| 18 | |
| 19 let (hostname, port) = string.split_at(colon); | |
| 20 | |
| 21 if let Err(error) = port[1..].parse::<u16>() { | |
| 22 return Err(ResolvAddrError::InvalidPort { string: string.into(), error, }); | |
| 23 } | |
| 24 | |
| 25 let len = hostname.len(); | |
| 26 if len > 253 { | |
| 27 return Err(ResolvAddrError::TooLong { string: string.into(), len, } ) | |
| 28 } | |
| 29 | |
| 30 let mut label_start = 0usize; | |
| 31 | |
| 32 for (i, c) in hostname.chars().enumerate() { | |
| 33 match c { | |
| 34 '.' => { | |
| 35 if i - label_start == 0 { | |
| 36 return Err(ResolvAddrError::EmptyLabel { string: string.into(), label_start, }); | |
| 37 } | |
| 38 | |
| 39 label_start = i + 1; | |
| 40 }, | |
| 41 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' => (), | |
| 42 _ => return Err(ResolvAddrError::InvalidCharacter { string: string.into(), c, pos: i, }), | |
| 43 } | |
| 44 | |
| 45 if i - label_start > 63 { | |
| 46 return Err(ResolvAddrError::LongLabel { string: string.into(), label_start, label_end: i, }); | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 Ok(ResolvAddr(string.into())) | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 impl fmt::Display for ResolvAddr { | |
| 55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| 56 fmt::Display::fmt(&self.0, f) | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 | |
| 61 #[derive(Debug, Error)] | |
| 62 pub(crate) enum ResolvAddrError { | |
| 63 #[error("hostname {string} has {len} character which exceeds the limit of 253")] | |
| 64 TooLong { string: String, len: usize }, | |
| 65 #[error("invalid character {c} in hostname {string} at position {pos}")] | |
| 66 InvalidCharacter { string: String, pos: usize, c: char, }, | |
| 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)] | |
| 68 LongLabel { string: String, label_start: usize, label_end: usize, }, | |
| 69 #[error("hostname {string} contains an empty label at position {label_start}")] | |
| 70 EmptyLabel { string: String, label_start: usize, }, | |
| 71 #[error("the address {0} is missing a port")] | |
| 72 MissingPort(String), | |
| 73 #[error("failed to parse port numer in the address {string}")] | |
| 74 InvalidPort { string: String, error: std::num::ParseIntError, }, | |
| 75 } | 
