Mercurial > crates > systemd-socket
comparison src/lib.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 | 0edcde404b02 |
children | a7893294e9b2 |
comparison
equal
deleted
inserted
replaced
3:0edcde404b02 | 4:66c0e10c89fc |
---|---|
9 //! | 9 //! |
10 //! The provided type supports conversions from various types of strings and also `serde` and `parse_arg` via feature flag. | 10 //! The provided type supports conversions from various types of strings and also `serde` and `parse_arg` via feature flag. |
11 //! Thanks to this the change to your code should be minimal - parsing will continue to work, it'll just allow a new format. | 11 //! Thanks to this the change to your code should be minimal - parsing will continue to work, it'll just allow a new format. |
12 //! You only need to change the code to use `SocketAddr::bind()` instead of `TcpListener::bind()` for binding. | 12 //! You only need to change the code to use `SocketAddr::bind()` instead of `TcpListener::bind()` for binding. |
13 //! | 13 //! |
14 //! Further, the crate also provides convenience methods for binding `tokio` 0.2, 0.3, and | 14 //! Further, the crate also provides methods for binding `tokio` 0.2, 0.3, and `async_std` sockets if the appropriate features are |
15 //! `async_std` sockets if the appropriate features are activated. | 15 //! activated. |
16 //! | 16 //! |
17 //! ## Example | 17 //! ## Example |
18 //! | 18 //! |
19 //! ```no_run | 19 //! ```no_run |
20 //! use systemd_socket::SocketAddr; | 20 //! use systemd_socket::SocketAddr; |
39 //! | 39 //! |
40 //! ## Features | 40 //! ## Features |
41 //! | 41 //! |
42 //! * `serde` - implements `serde::Deserialize` for `SocketAddr` | 42 //! * `serde` - implements `serde::Deserialize` for `SocketAddr` |
43 //! * `parse_arg` - implements `parse_arg::ParseArg` for `SocketAddr` | 43 //! * `parse_arg` - implements `parse_arg::ParseArg` for `SocketAddr` |
44 //! * `tokio_0_2` - adds `bind_tokio_0_2` convenience method to `SocketAddr` | 44 //! * `tokio_0_2` - adds `bind_tokio_0_2` method to `SocketAddr` |
45 //! * `tokio_0_3` - adds `bind_tokio_0_3` convenience method to `SocketAddr` | 45 //! * `tokio_0_3` - adds `bind_tokio_0_3` method to `SocketAddr` |
46 //! * `async_std` - adds `bind_async_std` convenience method to `SocketAddr` | 46 //! * `async_std` - adds `bind_async_std` method to `SocketAddr` |
47 //! | 47 //! |
48 //! ## MSRV | 48 //! ## MSRV |
49 //! | 49 //! |
50 //! This crate must always compile with the latest Rust available in the latest Debian stable. | 50 //! This crate must always compile with the latest Rust available in the latest Debian stable. |
51 //! That is currently Rust 1.41.1. (Debian 10 - Buster) | 51 //! That is currently Rust 1.41.1. (Debian 10 - Buster) |
52 | 52 |
53 | 53 |
54 #![deny(missing_docs)] | 54 #![deny(missing_docs)] |
55 | 55 |
56 pub mod error; | 56 pub mod error; |
57 mod resolv_addr; | |
57 | 58 |
58 use std::convert::{TryFrom, TryInto}; | 59 use std::convert::{TryFrom, TryInto}; |
59 use std::fmt; | 60 use std::fmt; |
60 use std::ffi::{OsStr, OsString}; | 61 use std::ffi::{OsStr, OsString}; |
61 use crate::error::*; | 62 use crate::error::*; |
63 use crate::resolv_addr::ResolvAddr; | |
62 | 64 |
63 pub(crate) mod systemd_sockets { | 65 pub(crate) mod systemd_sockets { |
64 use std::fmt; | 66 use std::fmt; |
65 use std::sync::Mutex; | 67 use std::sync::Mutex; |
66 use libsystemd::activation::FileDescriptor; | 68 use libsystemd::activation::FileDescriptor; |
124 /// Creates `std::net::TcpListener` | 126 /// Creates `std::net::TcpListener` |
125 /// | 127 /// |
126 /// This method either `binds` the socket, if the address was provided or uses systemd socket | 128 /// This method either `binds` the socket, if the address was provided or uses systemd socket |
127 /// if the socket name was provided. | 129 /// if the socket name was provided. |
128 pub fn bind(self) -> Result<std::net::TcpListener, BindError> { | 130 pub fn bind(self) -> Result<std::net::TcpListener, BindError> { |
129 self._bind().map(|(socket, _)| socket) | 131 match self.0 { |
132 SocketAddrInner::Ordinary(addr) => match std::net::TcpListener::bind(addr) { | |
133 Ok(socket) => Ok(socket), | |
134 Err(error) => Err(BindErrorInner::BindFailed { addr, error, }.into()), | |
135 }, | |
136 SocketAddrInner::WithHostname(addr) => match std::net::TcpListener::bind(addr.as_str()) { | |
137 Ok(socket) => Ok(socket), | |
138 Err(error) => Err(BindErrorInner::BindOrResolvFailed { addr, error, }.into()), | |
139 }, | |
140 SocketAddrInner::Systemd(socket_name) => Self::get_systemd(socket_name).map(|(socket, _)| socket), | |
141 } | |
130 } | 142 } |
131 | 143 |
132 /// Creates `tokio::net::TcpListener` | 144 /// Creates `tokio::net::TcpListener` |
133 /// | 145 /// |
134 /// To be specific, it binds the socket and converts it to `tokio` 0.2 socket. | 146 /// To be specific, it binds the socket or converts systemd socket to `tokio` 0.2 socket. |
135 /// | 147 /// |
136 /// This method either `binds` the socket, if the address was provided or uses systemd socket | 148 /// This method either `binds` the socket, if the address was provided or uses systemd socket |
137 /// if the socket name was provided. | 149 /// if the socket name was provided. |
138 #[cfg(feature = "tokio_0_2")] | 150 #[cfg(feature = "tokio_0_2")] |
139 pub fn bind_tokio_0_2(self) -> Result<tokio_0_2::net::TcpListener, TokioBindError> { | 151 pub async fn bind_tokio_0_2(self) -> Result<tokio_0_2::net::TcpListener, TokioBindError> { |
140 let (socket, addr) = self._bind()?; | 152 match self.0 { |
141 socket.try_into().map_err(|error| TokioConversionError { addr, error, }.into()) | 153 SocketAddrInner::Ordinary(addr) => match tokio_0_2::net::TcpListener::bind(addr).await { |
154 Ok(socket) => Ok(socket), | |
155 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindFailed { addr, error, }.into())), | |
156 }, | |
157 SocketAddrInner::WithHostname(addr) => match tokio_0_2::net::TcpListener::bind(addr.as_str()).await { | |
158 Ok(socket) => Ok(socket), | |
159 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindOrResolvFailed { addr, error, }.into())), | |
160 }, | |
161 SocketAddrInner::Systemd(socket_name) => { | |
162 let (socket, addr) = Self::get_systemd(socket_name)?; | |
163 socket.try_into().map_err(|error| TokioConversionError { addr, error, }.into()) | |
164 }, | |
165 } | |
142 } | 166 } |
143 | 167 |
144 /// Creates `tokio::net::TcpListener` | 168 /// Creates `tokio::net::TcpListener` |
145 /// | 169 /// |
146 /// To be specific, it binds the socket and converts it to `tokio` 0.3 socket. | 170 /// To be specific, it binds the socket or converts systemd socket to `tokio` 0.3 socket. |
147 /// | 171 /// |
148 /// This method either `binds` the socket, if the address was provided or uses systemd socket | 172 /// This method either `binds` the socket, if the address was provided or uses systemd socket |
149 /// if the socket name was provided. | 173 /// if the socket name was provided. |
150 #[cfg(feature = "tokio_0_3")] | 174 #[cfg(feature = "tokio_0_3")] |
151 pub fn bind_tokio_0_3(self) -> Result<tokio_0_3::net::TcpListener, TokioBindError> { | 175 pub async fn bind_tokio_0_3(self) -> Result<tokio_0_3::net::TcpListener, TokioBindError> { |
152 let (socket, addr) = self._bind()?; | 176 match self.0 { |
153 socket.try_into().map_err(|error| TokioConversionError { addr, error, }.into()) | 177 SocketAddrInner::Ordinary(addr) => match tokio_0_3::net::TcpListener::bind(addr).await { |
178 Ok(socket) => Ok(socket), | |
179 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindFailed { addr, error, }.into())), | |
180 }, | |
181 SocketAddrInner::WithHostname(addr) => match tokio_0_3::net::TcpListener::bind(addr.as_str()).await { | |
182 Ok(socket) => Ok(socket), | |
183 Err(error) => Err(TokioBindError::Bind(BindErrorInner::BindOrResolvFailed { addr, error, }.into())), | |
184 }, | |
185 SocketAddrInner::Systemd(socket_name) => { | |
186 let (socket, addr) = Self::get_systemd(socket_name)?; | |
187 socket.try_into().map_err(|error| TokioConversionError { addr, error, }.into()) | |
188 }, | |
189 } | |
154 } | 190 } |
155 | 191 |
156 /// Creates `async_std::net::TcpListener` | 192 /// Creates `async_std::net::TcpListener` |
157 /// | 193 /// |
158 /// To be specific, it binds the socket and converts it to `async_std` socket. | 194 /// To be specific, it binds the socket or converts systemd socket to `async_std` socket. |
159 /// | 195 /// |
160 /// This method either `binds` the socket, if the address was provided or uses systemd socket | 196 /// This method either `binds` the socket, if the address was provided or uses systemd socket |
161 /// if the socket name was provided. | 197 /// if the socket name was provided. |
162 #[cfg(feature = "async-std")] | 198 #[cfg(feature = "async-std")] |
163 pub fn bind_async_std(self) -> Result<async_std::net::TcpListener, BindError> { | 199 pub async fn bind_async_std(self) -> Result<async_std::net::TcpListener, BindError> { |
164 let (socket, _) = self._bind()?; | 200 match self.0 { |
165 Ok(socket.into()) | 201 SocketAddrInner::Ordinary(addr) => match async_std::net::TcpListener::bind(addr).await { |
202 Ok(socket) => Ok(socket), | |
203 Err(error) => Err(BindErrorInner::BindFailed { addr, error, }.into()), | |
204 }, | |
205 SocketAddrInner::WithHostname(addr) => match async_std::net::TcpListener::bind(addr.as_str()).await { | |
206 Ok(socket) => Ok(socket), | |
207 Err(error) => Err(BindErrorInner::BindOrResolvFailed { addr, error, }.into()), | |
208 }, | |
209 SocketAddrInner::Systemd(socket_name) => { | |
210 let (socket, _) = Self::get_systemd(socket_name)?; | |
211 Ok(socket.into()) | |
212 }, | |
213 } | |
166 } | 214 } |
167 | 215 |
168 // We can't impl<T: Deref<Target=str> + Into<String>> TryFrom<T> for SocketAddr because of orphan | 216 // We can't impl<T: Deref<Target=str> + Into<String>> TryFrom<T> for SocketAddr because of orphan |
169 // rules. | 217 // rules. |
170 fn try_from_generic<'a, T>(string: T) -> Result<Self, ParseError> where T: 'a + std::ops::Deref<Target=str> + Into<String> { | 218 fn try_from_generic<'a, T>(string: T) -> Result<Self, ParseError> where T: 'a + std::ops::Deref<Target=str> + Into<String> { |
174 None if name_len <= 255 => Ok(SocketAddr(SocketAddrInner::Systemd(string.into()))), | 222 None if name_len <= 255 => Ok(SocketAddr(SocketAddrInner::Systemd(string.into()))), |
175 None => Err(ParseErrorInner::LongSocketName { string: string.into(), len: name_len }.into()), | 223 None => Err(ParseErrorInner::LongSocketName { string: string.into(), len: name_len }.into()), |
176 Some((pos, c)) => Err(ParseErrorInner::InvalidCharacter { string: string.into(), c, pos, }.into()), | 224 Some((pos, c)) => Err(ParseErrorInner::InvalidCharacter { string: string.into(), c, pos, }.into()), |
177 } | 225 } |
178 } else { | 226 } else { |
179 Ok(string.parse().map(SocketAddrInner::Ordinary).map(SocketAddr).map_err(ParseErrorInner::SocketAddr)?) | 227 match string.parse() { |
180 } | 228 Ok(addr) => Ok(SocketAddr(SocketAddrInner::Ordinary(addr))), |
181 } | 229 Err(_) => Ok(SocketAddr(SocketAddrInner::WithHostname(ResolvAddr::try_from_generic(string).map_err(ParseErrorInner::ResolvAddr)?))), |
182 | 230 } |
183 fn _bind(self) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> { | 231 } |
184 match self.0 { | 232 } |
185 SocketAddrInner::Ordinary(addr) => match std::net::TcpListener::bind(addr) { | 233 |
186 Ok(socket) => Ok((socket, SocketAddrInner::Ordinary(addr))), | 234 fn get_systemd(socket_name: String) -> Result<(std::net::TcpListener, SocketAddrInner), BindError> { |
187 Err(error) => Err(BindErrorInner::BindFailed { addr, error, }.into()), | 235 use libsystemd::activation::IsType; |
188 }, | 236 use std::os::unix::io::{FromRawFd, IntoRawFd}; |
189 SocketAddrInner::Systemd(socket_name) => { | 237 |
190 use libsystemd::activation::IsType; | 238 let socket = systemd_sockets::take(&socket_name[SYSTEMD_PREFIX.len()..]).map_err(BindErrorInner::ReceiveDescriptors)?; |
191 use std::os::unix::io::{FromRawFd, IntoRawFd}; | 239 // Safety: The environment variable is unset, so that no other calls can get the |
192 | 240 // descriptors. The descriptors are taken from the map, not cloned, so they can't |
193 let socket = systemd_sockets::take(&socket_name[SYSTEMD_PREFIX.len()..]).map_err(BindErrorInner::ReceiveDescriptors)?; | 241 // be duplicated. |
194 // Safety: The environment variable is unset, so that no other calls can get the | 242 unsafe { |
195 // descriptors. The descriptors are taken from the map, not cloned, so they can't | 243 // match instead of combinators to avoid cloning socket_name |
196 // be duplicated. | 244 match socket { |
197 unsafe { | 245 Some(socket) if socket.is_inet() => Ok((std::net::TcpListener::from_raw_fd(socket.into_raw_fd()), SocketAddrInner::Systemd(socket_name))), |
198 // match instead of combinators to avoid cloning socket_name | 246 Some(_) => Err(BindErrorInner::NotInetSocket(socket_name).into()), |
199 match socket { | 247 None => Err(BindErrorInner::MissingDescriptor(socket_name).into()) |
200 Some(socket) if socket.is_inet() => Ok((std::net::TcpListener::from_raw_fd(socket.into_raw_fd()), SocketAddrInner::Systemd(socket_name))), | 248 } |
201 Some(_) => Err(BindErrorInner::NotInetSocket(socket_name).into()), | |
202 None => Err(BindErrorInner::MissingDescriptor(socket_name).into()) | |
203 } | |
204 } | |
205 }, | |
206 } | 249 } |
207 } | 250 } |
208 } | 251 } |
209 | 252 |
210 /// Displays the address in format that can be parsed again. | 253 /// Displays the address in format that can be parsed again. |
220 impl fmt::Display for SocketAddrInner { | 263 impl fmt::Display for SocketAddrInner { |
221 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 264 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
222 match self { | 265 match self { |
223 SocketAddrInner::Ordinary(addr) => fmt::Display::fmt(addr, f), | 266 SocketAddrInner::Ordinary(addr) => fmt::Display::fmt(addr, f), |
224 SocketAddrInner::Systemd(addr) => fmt::Display::fmt(addr, f), | 267 SocketAddrInner::Systemd(addr) => fmt::Display::fmt(addr, f), |
268 SocketAddrInner::WithHostname(addr) => fmt::Display::fmt(addr, f), | |
225 } | 269 } |
226 } | 270 } |
227 } | 271 } |
228 | 272 |
229 // PartialEq for testing, I'm not convinced it should be exposed | 273 // PartialEq for testing, I'm not convinced it should be exposed |
230 #[derive(Debug, PartialEq)] | 274 #[derive(Debug, PartialEq)] |
231 enum SocketAddrInner { | 275 enum SocketAddrInner { |
232 Ordinary(std::net::SocketAddr), | 276 Ordinary(std::net::SocketAddr), |
277 WithHostname(resolv_addr::ResolvAddr), | |
233 Systemd(String), | 278 Systemd(String), |
234 } | 279 } |
235 | 280 |
236 const SYSTEMD_PREFIX: &str = "systemd://"; | 281 const SYSTEMD_PREFIX: &str = "systemd://"; |
237 | 282 |