Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring DNS to base trait #107

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading