From 52013c113fc0cf0d9af15e7dcfa4fb8162dc901b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 2 Jan 2024 10:57:16 +0100 Subject: [PATCH 1/4] Make all error enums non_exhaustive Allows adding more error variants without it being a breaking change --- src/statsd.rs | 1 + src/tcp2udp.rs | 1 + src/tcp_options.rs | 1 + src/udp2tcp.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/statsd.rs b/src/statsd.rs index c3a9655..ed69ad9 100644 --- a/src/statsd.rs +++ b/src/statsd.rs @@ -63,6 +63,7 @@ mod real { const PREFIX: &str = "tcp2udp"; #[derive(Debug)] + #[non_exhaustive] pub enum Error { /// Failed to create + bind the statsd UDP socket. BindUdpSocket(std::io::Error), diff --git a/src/tcp2udp.rs b/src/tcp2udp.rs index 0ebe2ae..90ce79c 100644 --- a/src/tcp2udp.rs +++ b/src/tcp2udp.rs @@ -44,6 +44,7 @@ pub struct Options { /// Error returned from [`run`] if something goes wrong. #[derive(Debug)] +#[non_exhaustive] pub enum Tcp2UdpError { /// No TCP listen addresses given in the `Options`. NoTcpListenAddrs, diff --git a/src/tcp_options.rs b/src/tcp_options.rs index 5dafe3b..3164bc9 100644 --- a/src/tcp_options.rs +++ b/src/tcp_options.rs @@ -35,6 +35,7 @@ pub struct TcpOptions { } #[derive(Debug)] +#[non_exhaustive] pub enum ApplyTcpOptionsError { /// Failed to get/set TCP_RCVBUF RecvBuffer(io::Error), diff --git a/src/udp2tcp.rs b/src/udp2tcp.rs index 78512c9..6da22cd 100644 --- a/src/udp2tcp.rs +++ b/src/udp2tcp.rs @@ -11,6 +11,7 @@ use tokio::net::{TcpSocket, UdpSocket}; use std::os::unix::io::{AsRawFd, RawFd}; #[derive(Debug)] +#[non_exhaustive] pub enum Error { /// Failed to create the TCP socket. CreateTcpSocket(io::Error), From c9ef29e4a6cf86e3b95a5184be44287c951fc7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 2 Jan 2024 11:02:36 +0100 Subject: [PATCH 2/4] Make statsd_host field public --- src/tcp2udp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tcp2udp.rs b/src/tcp2udp.rs index 90ce79c..40dff72 100644 --- a/src/tcp2udp.rs +++ b/src/tcp2udp.rs @@ -39,7 +39,7 @@ pub struct Options { #[cfg(feature = "statsd")] /// Host to send statsd metrics to. #[cfg_attr(feature = "clap", clap(long))] - statsd_host: Option, + pub statsd_host: Option, } /// Error returned from [`run`] if something goes wrong. From c4c2528ffb3e16ea5363e6974c852393499ab47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 2 Jan 2024 14:39:30 +0100 Subject: [PATCH 3/4] Make tcp2udp::Options non_exhaustive to not have API breakage later --- src/tcp2udp.rs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/tcp2udp.rs b/src/tcp2udp.rs index 40dff72..a971aa7 100644 --- a/src/tcp2udp.rs +++ b/src/tcp2udp.rs @@ -16,9 +16,15 @@ use tokio::time::sleep; #[path = "statsd.rs"] mod statsd; -#[derive(Debug)] +/// Settings for a tcp2udp session. This is the argument to [`run`] to +/// describe how the forwarding from TCP -> UDP should be set up. +/// +/// This struct is `non_exhaustive` in order to allow adding more optional fields without +/// being considered breaking changes. So you need to create an instance via [`Options::new`]. +#[derive(Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Parser))] #[cfg_attr(feature = "clap", group(skip))] +#[non_exhaustive] pub struct Options { /// The IP and TCP port(s) to listen to for incoming traffic from udp2tcp. /// Supports binding multiple TCP sockets. @@ -42,6 +48,38 @@ pub struct Options { pub statsd_host: Option, } +impl Options { + /// Creates a new [`Options`] with all mandatory fields set to the passed arguments. + /// All optional values are set to their default values. They can later be set, since + /// they are public. + /// + /// # Examples + /// + /// ``` + /// # use std::net::{IpAddr, Ipv4Addr, SocketAddrV4, SocketAddr}; + /// + /// let mut options = udp_over_tcp::tcp2udp::Options::new( + /// // Listen on 127.0.0.1:1234/TCP + /// vec![SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 1234))], + /// // Forward to 192.0.2.15:5001/UDP + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 0, 2, 15), 5001)), + /// ); + /// + /// // Bind the local UDP socket (used to send to 192.0.2.15:5001/UDP) to the loopback interface + /// options.udp_bind_ip = Some(IpAddr::V4(Ipv4Addr::LOCALHOST)); + /// ``` + pub fn new(tcp_listen_addrs: Vec, udp_forward_addr: SocketAddr) -> Self { + Options { + tcp_listen_addrs, + udp_forward_addr, + udp_bind_ip: None, + tcp_options: Default::default(), + #[cfg(feature = "statsd")] + statsd_host: None, + } + } +} + /// Error returned from [`run`] if something goes wrong. #[derive(Debug)] #[non_exhaustive] From c60b3e901fc6a815f5e75716a63aadb24b5d1b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Tue, 2 Jan 2024 14:43:01 +0100 Subject: [PATCH 4/4] Make TcpOptions non_exhaustive --- src/tcp_options.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tcp_options.rs b/src/tcp_options.rs index 3164bc9..8f91d5c 100644 --- a/src/tcp_options.rs +++ b/src/tcp_options.rs @@ -8,6 +8,7 @@ use tokio::net::{TcpSocket, TcpStream}; /// Options to apply to the TCP socket involved in the tunneling. #[derive(Debug, Default, Clone)] #[cfg_attr(feature = "clap", derive(clap::Parser))] +#[non_exhaustive] pub struct TcpOptions { /// If given, sets the SO_RCVBUF option on the TCP socket to the given number of bytes. /// Changes the size of the operating system's receive buffer associated with the socket.