use crate::error;
use dns_lookup::{AddrInfo, AddrInfoHints, AddrInfoIter, LookupError};
use socket2::SockAddr;
#[cfg(unix)]
use libc::AF_INET;
#[cfg(all(unix, feature = "sync-sender-qwp-udp"))]
use libc::SOCK_DGRAM;
#[cfg(all(unix, feature = "sync-sender-tcp"))]
use libc::SOCK_STREAM;
#[cfg(windows)]
use windows_sys::Win32::Networking::WinSock::AF_INET;
#[cfg(all(windows, feature = "sync-sender-qwp-udp"))]
use windows_sys::Win32::Networking::WinSock::SOCK_DGRAM;
#[cfg(all(windows, feature = "sync-sender-tcp"))]
use windows_sys::Win32::Networking::WinSock::SOCK_STREAM;
#[cfg(unix)]
const ADDR_FAMILY_INET: i32 = AF_INET;
#[cfg(windows)]
const ADDR_FAMILY_INET: i32 = AF_INET as i32;
fn map_getaddrinfo_result(
dest: &str,
result: Result<AddrInfoIter, LookupError>,
) -> crate::Result<SockAddr> {
match result {
Ok(mut addrs) => map_first_addrinfo(dest, addrs.next()),
Err(lookup_err) => {
let io_err: std::io::Error = lookup_err.into();
Err(error::fmt!(
CouldNotResolveAddr,
"Could not resolve {:?}: {}",
dest,
io_err
))
}
}
}
fn map_first_addrinfo(
dest: &str,
result: Option<std::io::Result<AddrInfo>>,
) -> crate::Result<SockAddr> {
match result {
Some(Ok(addr)) => Ok(addr.sockaddr.into()),
Some(Err(io_err)) => Err(error::fmt!(
CouldNotResolveAddr,
"Could not resolve {:?}: {}",
dest,
io_err
)),
None => Err(error::fmt!(
CouldNotResolveAddr,
"Could not resolve {:?}: no addresses returned",
dest
)),
}
}
#[cfg(feature = "sync-sender-tcp")]
pub(super) fn resolve_host(host: &str) -> super::Result<SockAddr> {
resolve_host_with_socktype(host, SOCK_STREAM)
}
#[cfg(feature = "sync-sender-qwp-udp")]
pub(super) fn resolve_host_udp(host: &str) -> super::Result<SockAddr> {
resolve_host_with_socktype(host, SOCK_DGRAM)
}
fn resolve_host_with_socktype(host: &str, socktype: i32) -> super::Result<SockAddr> {
let hints = AddrInfoHints {
socktype,
address: ADDR_FAMILY_INET,
..AddrInfoHints::default()
};
map_getaddrinfo_result(host, dns_lookup::getaddrinfo(Some(host), None, Some(hints)))
}
#[cfg(feature = "sync-sender-tcp")]
pub(super) fn resolve_host_port(host: &str, port: &str) -> super::Result<SockAddr> {
resolve_host_port_with_socktype(host, port, SOCK_STREAM)
}
#[cfg(feature = "sync-sender-qwp-udp")]
pub(super) fn resolve_host_port_udp(host: &str, port: &str) -> super::Result<SockAddr> {
resolve_host_port_with_socktype(host, port, SOCK_DGRAM)
}
fn resolve_host_port_with_socktype(
host: &str,
port: &str,
socktype: i32,
) -> super::Result<SockAddr> {
let hints = AddrInfoHints {
socktype,
address: ADDR_FAMILY_INET,
..AddrInfoHints::default()
};
let host_port = format!("{host}:{port}");
map_getaddrinfo_result(
&host_port,
dns_lookup::getaddrinfo(Some(host), Some(port), Some(hints)),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ErrorCode;
#[test]
fn empty_getaddrinfo_result_is_error() {
let err = map_first_addrinfo("example.invalid:9009", None).unwrap_err();
assert_eq!(err.code(), ErrorCode::CouldNotResolveAddr);
assert!(
err.msg()
.contains("Could not resolve \"example.invalid:9009\": no addresses returned")
);
}
}