| use crate::io; |
| use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; |
| use crate::os::xous::ffi::lend_mut; |
| use crate::os::xous::services::{dns_server, DnsLendMut}; |
| use core::convert::{TryFrom, TryInto}; |
| |
| pub struct DnsError { |
| pub code: u8, |
| } |
| |
| #[repr(C, align(4096))] |
| struct LookupHostQuery([u8; 4096]); |
| |
| pub struct LookupHost { |
| data: LookupHostQuery, |
| port: u16, |
| offset: usize, |
| count: usize, |
| } |
| |
| impl LookupHost { |
| pub fn port(&self) -> u16 { |
| self.port |
| } |
| } |
| |
| impl Iterator for LookupHost { |
| type Item = SocketAddr; |
| fn next(&mut self) -> Option<SocketAddr> { |
| if self.offset >= self.data.0.len() { |
| return None; |
| } |
| match self.data.0.get(self.offset) { |
| Some(&4) => { |
| self.offset += 1; |
| if self.offset + 4 > self.data.0.len() { |
| return None; |
| } |
| let result = Some(SocketAddr::V4(SocketAddrV4::new( |
| Ipv4Addr::new( |
| self.data.0[self.offset], |
| self.data.0[self.offset + 1], |
| self.data.0[self.offset + 2], |
| self.data.0[self.offset + 3], |
| ), |
| self.port, |
| ))); |
| self.offset += 4; |
| result |
| } |
| Some(&6) => { |
| self.offset += 1; |
| if self.offset + 16 > self.data.0.len() { |
| return None; |
| } |
| let mut new_addr = [0u8; 16]; |
| for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)] |
| .iter() |
| .zip(new_addr.iter_mut()) |
| { |
| *octet = *src; |
| } |
| let result = |
| Some(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.port, 0, 0))); |
| self.offset += 16; |
| result |
| } |
| _ => None, |
| } |
| } |
| } |
| |
| pub fn lookup(query: &str, port: u16) -> Result<LookupHost, DnsError> { |
| let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port }; |
| |
| // Copy the query into the message that gets sent to the DNS server |
| for (query_byte, result_byte) in query.as_bytes().iter().zip(result.data.0.iter_mut()) { |
| *result_byte = *query_byte; |
| } |
| |
| lend_mut( |
| dns_server(), |
| DnsLendMut::RawLookup.into(), |
| &mut result.data.0, |
| 0, |
| query.as_bytes().len(), |
| ) |
| .unwrap(); |
| if result.data.0[0] != 0 { |
| return Err(DnsError { code: result.data.0[1] }); |
| } |
| assert_eq!(result.offset, 0); |
| result.count = result.data.0[1] as usize; |
| |
| // Advance the offset to the first record |
| result.offset = 2; |
| Ok(result) |
| } |
| |
| impl TryFrom<&str> for LookupHost { |
| type Error = io::Error; |
| |
| fn try_from(s: &str) -> io::Result<LookupHost> { |
| macro_rules! try_opt { |
| ($e:expr, $msg:expr) => { |
| match $e { |
| Some(r) => r, |
| None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &$msg)), |
| } |
| }; |
| } |
| |
| // split the string by ':' and convert the second part to u16 |
| let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); |
| let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); |
| (host, port).try_into() |
| } |
| } |
| |
| impl TryFrom<(&str, u16)> for LookupHost { |
| type Error = io::Error; |
| |
| fn try_from(v: (&str, u16)) -> io::Result<LookupHost> { |
| lookup(v.0, v.1) |
| .map_err(|_e| io::const_io_error!(io::ErrorKind::InvalidInput, &"DNS failure")) |
| } |
| } |