Skip to content

Commit

Permalink
Refactoring DNS to base trait
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-summers committed Jul 24, 2024
1 parent 8b39b4d commit 813d7e9
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 119 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Bump MSRV to 1.77.0 for `ip_in_core`.
- Removed the `no-std-net` and `ip_in_core` features, `ip_in_core` is now the default.

## Changed
- [breaking] The DNS traits have been removed and placed into the default `NetworkStack`
implementation. All stacks must implement these traits (but may implement them as `Unsupported`
features.

## [0.8.0] - 2023-11-10

- Bump MSRV to 1.60.0 (required for Edition 2021)
Expand Down
75 changes: 0 additions & 75 deletions src/dns.rs

This file was deleted.

100 changes: 96 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,105 @@
#![deny(missing_docs)]
#![deny(unsafe_code)]

mod dns;
mod stack;

use core::net::IpAddr;
pub use nb;

pub use dns::{AddrType, Dns};
pub use stack::{
SharableStack, SharedStack, TcpClientStack, TcpError, TcpErrorKind, TcpFullStack,
UdpClientStack, UdpFullStack,
SharableStack, SharedStack, TcpClientStack, TcpFullStack, UdpClientStack, UdpFullStack,
};

/// This is the host address type to be returned by `gethostbyname`.
///
/// An IPv4 address type always looks for `A` records, while IPv6 address type
/// will look for `AAAA` records
#[derive(Clone, Debug, PartialEq)]
pub enum AddrType {
/// Result is `A` record
IPv4,
/// Result is `AAAA` record
IPv6,
/// Result is either a `A` record, or a `AAAA` record
Either,
}

/// Represents specific errors encountered during TCP operations.
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ErrorKind {
/// The socket has been closed in the direction in which the failing operation was attempted.
PipeClosed,

/// The operation requested is not supported.
Unsupported,

/// Some other error has occurred.
Other,
}

/// Marker trait for errors that can be resolved to predefined categorical types.
pub trait Error: core::fmt::Debug {
/// Determine the type of error that occurred.
fn kind(&self) -> ErrorKind;
}

/// This trait is an extension trait for [`TcpStack`] and [`UdpStack`] for dns
/// resolutions. It does not handle every DNS record type, but is meant as an
/// embedded alternative to [`ToSocketAddrs`], and is as such meant to resolve
/// an ip address from a hostname, or a hostname from an ip address. This means
/// that it only deals in host address records `A` (IPv4) and `AAAA` (IPv6).
///
/// [`TcpStack`]: crate::trait@TcpStack
/// [`UdpStack`]: crate::trait@UdpStack
/// [`ToSocketAddrs`]:
/// https://doc.rust-lang.org/std/net/trait.ToSocketAddrs.html
pub trait NetworkStack {
/// The type returned when we have an error
type Error: Error;

/// Resolve the first ip address of a host, given its hostname and a desired
/// address record type to look for
fn get_host_by_name(
&mut self,
hostname: &str,
addr_type: AddrType,
) -> nb::Result<IpAddr, Self::Error>;

/// Resolve the hostname of a host, given its ip address.
///
/// The hostname is stored at the beginning of `result`, the length is returned.
///
/// If the buffer is too small to hold the domain name, an error should be returned.
///
/// **Note**: A fully qualified domain name (FQDN), has a maximum length of
/// 255 bytes according to [`rfc1035`]. Therefore, you can pass a 255-byte long
/// buffer to guarantee it'll always be large enough.
///
/// [`rfc1035`]: https://tools.ietf.org/html/rfc1035
fn get_host_by_address(
&mut self,
addr: IpAddr,
result: &mut [u8],
) -> nb::Result<usize, Self::Error>;
}

impl<T: NetworkStack> NetworkStack for &mut T {
type Error = T::Error;

fn get_host_by_name(
&mut self,
hostname: &str,
addr_type: AddrType,
) -> nb::Result<IpAddr, Self::Error> {
T::get_host_by_name(self, hostname, addr_type)
}

fn get_host_by_address(
&mut self,
addr: IpAddr,
result: &mut [u8],
) -> nb::Result<usize, Self::Error> {
T::get_host_by_address(self, addr, result)
}
}
2 changes: 1 addition & 1 deletion src/stack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ mod tcp;
mod udp;

pub use share::{SharableStack, SharedStack};
pub use tcp::{TcpClientStack, TcpError, TcpErrorKind, TcpFullStack};
pub use tcp::{TcpClientStack, TcpFullStack};
pub use udp::{UdpClientStack, UdpFullStack};
34 changes: 22 additions & 12 deletions src/stack/share.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{nb, TcpClientStack, TcpFullStack, UdpClientStack, UdpFullStack};
use crate::{
nb, AddrType, IpAddr, NetworkStack, TcpClientStack, TcpFullStack, UdpClientStack, UdpFullStack,
};
use core::cell::RefCell;
use core::net::SocketAddr;

Expand Down Expand Up @@ -97,17 +99,26 @@ macro_rules! forward {
}
}

impl<'a, T> NetworkStack for SharedStack<'a, T>
where
T: NetworkStack,
{
type Error = T::Error;

forward! {get_host_by_name(hostname: &str, addr_type: AddrType) -> nb::Result<IpAddr, Self::Error>}
forward! {get_host_by_address(addr: IpAddr, result: &mut [u8]) -> nb::Result<usize, Self::Error>}
}

impl<'a, T> UdpClientStack for SharedStack<'a, T>
where
T: UdpClientStack,
{
type Error = T::Error;
type UdpSocket = T::UdpSocket;

forward! {socket() -> Result<Self::UdpSocket, Self::Error>}
forward! {connect(socket: &mut Self::UdpSocket, address: SocketAddr) -> Result<(), Self::Error>}
forward! {send(socket: &mut Self::UdpSocket, data: &[u8]) -> Result<(), nb::Error<<T as UdpClientStack>::Error>>}
forward! {receive(socket: &mut Self::UdpSocket, data: &mut [u8]) -> Result<(usize, SocketAddr), nb::Error<<T as UdpClientStack>::Error>>}
forward! {send(socket: &mut Self::UdpSocket, data: &[u8]) -> Result<(), nb::Error<T::Error>>}
forward! {receive(socket: &mut Self::UdpSocket, data: &mut [u8]) -> Result<(usize, SocketAddr), nb::Error<T::Error>>}
forward! {close(socket: Self::UdpSocket) -> Result<(), Self::Error>}
}

Expand All @@ -116,28 +127,27 @@ where
T: UdpFullStack,
{
forward! {bind(socket: &mut Self::UdpSocket, local_port: u16) -> Result<(), Self::Error>}
forward! {send_to(socket: &mut Self::UdpSocket, remote: SocketAddr, buffer: &[u8]) -> Result<(), nb::Error<<T as UdpClientStack>::Error>>}
forward! {send_to(socket: &mut Self::UdpSocket, remote: SocketAddr, buffer: &[u8]) -> Result<(), nb::Error<T::Error>>}
}

impl<'a, T> TcpClientStack for SharedStack<'a, T>
where
T: TcpClientStack,
{
type TcpSocket = T::TcpSocket;
type Error = T::Error;

forward! {socket() -> Result<Self::TcpSocket, Self::Error>}
forward! {connect(socket: &mut Self::TcpSocket, address: SocketAddr) -> Result<(), nb::Error<<T as TcpClientStack>::Error>>}
forward! {send(socket: &mut Self::TcpSocket, data: &[u8]) -> Result<usize, nb::Error<<T as TcpClientStack>::Error>>}
forward! {receive(socket: &mut Self::TcpSocket, data: &mut [u8]) -> Result<usize, nb::Error<<T as TcpClientStack>::Error>>}
forward! {connect(socket: &mut Self::TcpSocket, address: SocketAddr) -> Result<(), nb::Error<T::Error>>}
forward! {send(socket: &mut Self::TcpSocket, data: &[u8]) -> Result<usize, nb::Error<T::Error>>}
forward! {receive(socket: &mut Self::TcpSocket, data: &mut [u8]) -> Result<usize, nb::Error<T::Error>>}
forward! {close(socket: Self::TcpSocket) -> Result<(), Self::Error>}
}

impl<'a, T> TcpFullStack for SharedStack<'a, T>
where
T: TcpFullStack,
{
forward! {bind(socket: &mut Self::TcpSocket, port: u16) -> Result<(), <T as TcpClientStack>::Error>}
forward! {listen(socket: &mut Self::TcpSocket) -> Result<(), <T as TcpClientStack>::Error>}
forward! {accept(socket: &mut Self::TcpSocket) -> Result<(<T as TcpClientStack>::TcpSocket, SocketAddr), nb::Error<<T as TcpClientStack>::Error>>}
forward! {bind(socket: &mut Self::TcpSocket, port: u16) -> Result<(), T::Error>}
forward! {listen(socket: &mut Self::TcpSocket) -> Result<(), T::Error>}
forward! {accept(socket: &mut Self::TcpSocket) -> Result<(T::TcpSocket, SocketAddr), nb::Error<T::Error>>}
}
24 changes: 2 additions & 22 deletions src/stack/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
use crate::NetworkStack;
use core::net::SocketAddr;

/// Represents specific errors encountered during TCP operations.
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TcpErrorKind {
/// The socket has been closed in the direction in which the failing operation was attempted.
PipeClosed,

/// Some other error has occurred.
Other,
}

/// Methods to resolve errors into identifiable, actionable codes on the client side.
pub trait TcpError: core::fmt::Debug {
/// Determines the kind of error that occurred.
fn kind(&self) -> TcpErrorKind;
}

/// This trait is implemented by TCP/IP stacks. You could, for example, have an implementation
/// which knows how to send AT commands to an ESP8266 WiFi module. You could have another implementation
/// which knows how to driver the Rust Standard Library's `std::net` module. Given this trait, you can
/// write a portable HTTP client which can work with either implementation.
pub trait TcpClientStack {
pub trait TcpClientStack: NetworkStack {
/// The type returned when we create a new TCP socket
type TcpSocket;
/// The type returned when we have an error
type Error: TcpError;

/// Open a socket for usage as a TCP client.
///
Expand Down Expand Up @@ -96,8 +78,6 @@ pub trait TcpFullStack: TcpClientStack {
}

impl<T: TcpClientStack> TcpClientStack for &mut T {
type Error = T::Error;

type TcpSocket = T::TcpSocket;

fn socket(&mut self) -> Result<Self::TcpSocket, Self::Error> {
Expand Down
7 changes: 2 additions & 5 deletions src/stack/udp.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use crate::NetworkStack;
use core::net::SocketAddr;

/// This trait is implemented by UDP/IP stacks. You could, for example, have
/// an implementation which knows how to send AT commands to an ESP8266 WiFi
/// module. You could have another implementation which knows how to driver the
/// Rust Standard Library's `std::net` module. Given this trait, you can how
/// write a portable CoAP client which can work with either implementation.
pub trait UdpClientStack {
pub trait UdpClientStack: NetworkStack {
/// The type returned when we create a new UDP socket
type UdpSocket;
/// The type returned when we have an error
type Error: core::fmt::Debug;

/// Allocate a socket for further use.
fn socket(&mut self) -> Result<Self::UdpSocket, Self::Error>;
Expand Down Expand Up @@ -61,8 +60,6 @@ pub trait UdpFullStack: UdpClientStack {
}

impl<T: UdpClientStack> UdpClientStack for &mut T {
type Error = T::Error;

type UdpSocket = T::UdpSocket;

fn socket(&mut self) -> Result<Self::UdpSocket, Self::Error> {
Expand Down

0 comments on commit 813d7e9

Please sign in to comment.