Mercurial > crates > systemd-socket
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/resolv_addr.rs Fri Nov 27 15:05:19 2020 +0100 @@ -0,0 +1,75 @@ +use thiserror::Error; +use std::fmt; + +#[derive(Debug, PartialEq)] +pub(crate) struct ResolvAddr(String); + +impl ResolvAddr { + pub(crate) fn as_str(&self) -> &str { + &self.0 + } + + pub(crate) fn try_from_generic<T: std::ops::Deref<Target=str> + Into<String>>(string: T) -> Result<Self, ResolvAddrError> { + // can't use a combinator due to borrowing + let colon = match string.rfind(':') { + Some(colon) => colon, + None => return Err(ResolvAddrError::MissingPort(string.into())), + }; + + let (hostname, port) = string.split_at(colon); + + if let Err(error) = port[1..].parse::<u16>() { + return Err(ResolvAddrError::InvalidPort { string: string.into(), error, }); + } + + let len = hostname.len(); + if len > 253 { + return Err(ResolvAddrError::TooLong { string: string.into(), len, } ) + } + + let mut label_start = 0usize; + + for (i, c) in hostname.chars().enumerate() { + match c { + '.' => { + if i - label_start == 0 { + return Err(ResolvAddrError::EmptyLabel { string: string.into(), label_start, }); + } + + label_start = i + 1; + }, + 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' => (), + _ => return Err(ResolvAddrError::InvalidCharacter { string: string.into(), c, pos: i, }), + } + + if i - label_start > 63 { + return Err(ResolvAddrError::LongLabel { string: string.into(), label_start, label_end: i, }); + } + } + + Ok(ResolvAddr(string.into())) + } +} + +impl fmt::Display for ResolvAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + + +#[derive(Debug, Error)] +pub(crate) enum ResolvAddrError { + #[error("hostname {string} has {len} character which exceeds the limit of 253")] + TooLong { string: String, len: usize }, + #[error("invalid character {c} in hostname {string} at position {pos}")] + InvalidCharacter { string: String, pos: usize, c: char, }, + #[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)] + LongLabel { string: String, label_start: usize, label_end: usize, }, + #[error("hostname {string} contains an empty label at position {label_start}")] + EmptyLabel { string: String, label_start: usize, }, + #[error("the address {0} is missing a port")] + MissingPort(String), + #[error("failed to parse port numer in the address {string}")] + InvalidPort { string: String, error: std::num::ParseIntError, }, +}