diff --git a/Cargo.lock b/Cargo.lock index 8c2d823..a806a77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bumpalo" version = "3.16.0" @@ -50,6 +56,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -70,6 +82,16 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "ctrlc" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +dependencies = [ + "nix", + "windows-sys", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -121,6 +143,7 @@ version = "0.1.0" dependencies = [ "apputils", "chrono", + "ctrlc", "serde", "toml", ] @@ -152,6 +175,18 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -335,6 +370,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 0c92fed..cf8ccf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "inetdx" -description = "Rust implementation of inetd internal services" +description = "Rust implementation of inetd trivial services" version = "0.1.0" edition = "2021" @@ -8,4 +8,5 @@ edition = "2021" apputils = "0.1.5" toml = "0.8" serde = { version = "1", features = ["derive"] } -chrono = "0.4" \ No newline at end of file +chrono = "0.4" +ctrlc = { version = "3.4", features = ["termination"] } \ No newline at end of file diff --git a/README.md b/README.md index fd3837f..f4fb357 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,5 @@ # inetdx +Rust implementation of inetd trivial services -Rust implementation of inetd internal services - -| Service | Port | RFC | Notes | -| -------- | ---- | ------- | ------------------------------------ | -| Echo | 7 | RFC 862 | - | -| Discard | 9 | RRC 863 | - | -| Daytime | 13 | RFC 867 | RFC 2822 timestamp | -| QotD | 17 | RFC 865 | Custom list or /etc/motd as fallback | -| Chargen | 19 | RFC 864 | - | -| Time | 37 | RFC 868 | Unix Timestamp as u64 | -| Hostname | 42 | - | cat /etc/hostname over TCP+UDP | \ No newline at end of file + +This is supposed to be a rather simple project, but I'm open for any feature requests, so just [open an issue](https://github.com/Stridsvagn69420/inetdx/issues/new). \ No newline at end of file diff --git a/src/chargen.rs b/src/chargen.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/daytime.rs b/src/daytime.rs index 936d114..85457dc 100644 --- a/src/daytime.rs +++ b/src/daytime.rs @@ -1,16 +1,15 @@ -use std::net::TcpStream; use std::net::IpAddr; use std::io::Write; use std::io; use chrono::Local; use crate::shared::{tcp_server, udp_server, BUFFER_SIZE_UDP}; -pub(crate) const NAME: &str = "Daytime"; -pub(crate) const PORT: u16 = 13; +const NAME: &str = "Daytime"; +const PORT: u16 = 13; /// Daytime TCP-Server -pub(crate) fn daytime_tcp(addr: IpAddr, port: u16) -> io::Result<()> { - tcp_server(addr, port, NAME, move |stream: &mut TcpStream| { +pub(crate) fn daytime_tcp(addr: IpAddr) -> io::Result<()> { + tcp_server(addr, PORT, NAME, move |mut stream| { // Write RFC 2822 timestamp let time = timestamp(); stream.write_all(time.as_bytes())?; @@ -19,8 +18,8 @@ pub(crate) fn daytime_tcp(addr: IpAddr, port: u16) -> io::Result<()> { } /// Daytime UDP-Server -pub(crate) fn daytime_udp(addr: IpAddr, port: u16) -> io::Result<()> { - udp_server(addr, port, NAME, |socket| { +pub(crate) fn daytime_udp(addr: IpAddr) -> io::Result<()> { + udp_server(addr, PORT, NAME, |socket| { // Accept Datagram let mut buf = [0; BUFFER_SIZE_UDP]; let (_, peer) = socket.recv_from(&mut buf)?; diff --git a/src/discard.rs b/src/discard.rs index e69d998..cc67090 100644 --- a/src/discard.rs +++ b/src/discard.rs @@ -3,12 +3,12 @@ use std::io::Read; use std::io; use crate::shared::{tcp_server, udp_server, BUFFER_SIZE_TCP, BUFFER_SIZE_UDP}; -pub(crate) const NAME: &str = "Discard"; -pub(crate) const PORT: u16 = 9; +const NAME: &str = "Discard"; +const PORT: u16 = 9; /// Discard TCP-Server -pub(crate) fn discard_tcp(addr: IpAddr, port: u16) -> io::Result<()> { - tcp_server(addr, port, NAME, |stream| { +pub(crate) fn discard_tcp(addr: IpAddr) -> io::Result<()> { + tcp_server(addr, PORT, NAME, |mut stream| { loop { // Read client data and ignore it like a boss let mut buf = [0; BUFFER_SIZE_TCP]; @@ -22,17 +22,11 @@ pub(crate) fn discard_tcp(addr: IpAddr, port: u16) -> io::Result<()> { } /// Discard UDP-Server -pub(crate) fn discard_udp(addr: IpAddr, port: u16) -> io::Result<()> { - udp_server(addr, port, NAME, |socket| { +pub(crate) fn discard_udp(addr: IpAddr) -> io::Result<()> { + udp_server(addr, PORT, NAME, |socket| { // Accept Datagram let mut buf = [0; BUFFER_SIZE_UDP]; - let (n, peer) = socket.recv_from(&mut buf)?; - - // Debug Code - if cfg!(debug_assertions) { - let data = String::from_utf8_lossy(&buf[..n]); - println!("DEBUG [{NAME}] Received {n} Bytes from {peer}: {data}"); - } + let _ = socket.recv_from(&mut buf)?; Ok(()) }) } \ No newline at end of file diff --git a/src/echo.rs b/src/echo.rs index c34d5d9..5955967 100644 --- a/src/echo.rs +++ b/src/echo.rs @@ -3,14 +3,14 @@ use std::io::{Read, Write}; use std::io; use crate::shared::{tcp_server, udp_server, BUFFER_SIZE_TCP}; -pub(crate) const NAME: &str = "Echo"; -pub(crate) const PORT: u16 = 7; +const NAME: &str = "Echo"; +const PORT: u16 = 7; const BUFFER_SIZE_UDP: usize = 65507; /// Echo TCP-Server -pub(crate) fn echo_tcp(addr: IpAddr, port: u16) -> io::Result<()> { - tcp_server(addr, port, NAME, |stream| { +pub(crate) fn echo_tcp(addr: IpAddr) -> io::Result<()> { + tcp_server(addr, PORT, NAME, |mut stream| { loop { // Read client data and write it back let mut buf = [0; BUFFER_SIZE_TCP]; @@ -18,6 +18,7 @@ pub(crate) fn echo_tcp(addr: IpAddr, port: u16) -> io::Result<()> { if n == 0 { break; } + stream.write_all(&buf[..n])?; } Ok(()) @@ -25,18 +26,12 @@ pub(crate) fn echo_tcp(addr: IpAddr, port: u16) -> io::Result<()> { } /// Echo UDP-Server -pub(crate) fn echo_udp(addr: IpAddr, port: u16) -> io::Result<()> { - udp_server(addr, port, NAME, |socket| { +pub(crate) fn echo_udp(addr: IpAddr) -> io::Result<()> { + udp_server(addr, PORT, NAME, |socket| { // Accept Datagram let mut buf = [0; BUFFER_SIZE_UDP]; let (n, peer) = socket.recv_from(&mut buf)?; - // Debug Code - if cfg!(debug_assertions) { - let data = String::from_utf8_lossy(&buf[..n]); - println!("DEBUG [{NAME}] Received {n} Bytes from {peer}: {data}"); - } - // Return if data was sent if n > 0 { socket.send_to(&buf[..n], peer)?; diff --git a/src/hostname.rs b/src/hostname.rs index f9377e7..4744760 100644 --- a/src/hostname.rs +++ b/src/hostname.rs @@ -1,4 +1,3 @@ -use std::net::TcpStream; use std::net::IpAddr; use std::io::Write; use std::io; @@ -10,28 +9,32 @@ use std::fs; #[cfg(not(target_family = "unix"))] use std::env; -pub(crate) const NAME: &str = "Hostname"; -pub(crate) const PORT: u16 = 42; +const NAME: &str = "Hostname"; +const PORT: u16 = 42; /// Hostname TCP-Server -pub(crate) fn hostname_tcp(addr: IpAddr, port: u16) -> io::Result<()> { - tcp_server(addr, port, NAME, move |stream: &mut TcpStream| { +pub(crate) fn hostname_tcp(addr: IpAddr) -> io::Result<()> { + // Read hostname once + let nodename = hostname()?; + + tcp_server(addr, PORT, NAME, move |mut stream| { // Write Hostname - let nodename = hostname()?; stream.write_all(nodename.as_bytes())?; Ok(()) }) } /// Hostname UDP-Server -pub(crate) fn hostname_udp(addr: IpAddr, port: u16) -> io::Result<()> { - udp_server(addr, port, NAME, |socket| { +pub(crate) fn hostname_udp(addr: IpAddr) -> io::Result<()> { + // Read hostname once + let nodename = hostname()?; + + udp_server(addr, PORT, NAME, move |socket| { // Accept Datagram let mut buf = [0; BUFFER_SIZE_UDP]; let (_, peer) = socket.recv_from(&mut buf)?; // Return Hostname - let nodename = hostname()?; socket.send_to(nodename.as_bytes(), peer)?; Ok(()) }) diff --git a/src/main.rs b/src/main.rs index 84355df..e59eb42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ use std::process::ExitCode; +use std::sync::mpsc; +use ctrlc; mod config; mod shared; mod echo; mod discard; mod daytime; -mod qotd; -mod chargen; mod time; mod hostname; @@ -17,7 +17,7 @@ fn main() -> ExitCode { println!(); // Configure example Server - if let Err(err) = echo::echo_udp(cfg.into(), echo::PORT) { + if let Err(err) = echo::echo_udp(cfg.into()) { println!("Could not start server ({}): {err}", err.kind()); ExitCode::FAILURE } else { diff --git a/src/qotd.rs b/src/qotd.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/shared.rs b/src/shared.rs index bff191d..8df05c5 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -20,7 +20,8 @@ pub(crate) const BUFFER_SIZE_TCP: usize = 4096; /// The default size for each UDP server's read buffer. /// /// This buffer is at the MTU for non-loopback LANs, because non-loopback networks usually have an MTU of around 1500 Bytes. -/// If the service does read directly from loopback, then use **65507** (not 65535 due to practical limitations); +/// If the service actually reads user input, use something like 65507. 65535 works too, but there is some overhead with IP packets, +/// so you're wasting a few bytes. /// /// The reason why buffer size matter is that [every datagram is a one-time read](UdpSocket::recv_from) /// and you cannot get the actual size without messing with heap memory. @@ -32,9 +33,9 @@ pub(crate) const BUFFER_SIZE_UDP: usize = 1500; /// - `addr`: The [IPv6 address](Ipv6Addr) to bind to. /// - `port`: The port to bind to. /// - `f`: The service handle function. -pub(crate) fn tcp_server(addr: IpAddr, port: u16, name: &str, mut f: F) -> io::Result<()> +pub(crate) fn tcp_server(addr: IpAddr, port: u16, name: &'static str, f: F) -> io::Result<()> where - F: FnMut(&mut TcpStream) -> io::Result<()> + Send + Copy + 'static + F: Fn(TcpStream) -> io::Result<()> + Send + Clone + 'static { // Create TCP Listener let listener = match TcpListener::bind((addr, port)) { @@ -50,21 +51,22 @@ where // Listener Loop loop { id += 1; - let mut connection = match listener.accept() { + let (conn, _) = match listener.accept() { Ok(conn) => { - println!("[Port {port}] ({id}) Connected to {}", conn.1); + println!("[{name}] ({id}) Connected to {}", conn.1); conn }, Err(err) => { - println!("[Port {port}] ({id}) TCP-Handshake failed ({}): {err}", err.kind()); + println!("[{name}] ({id}) TCP-Handshake failed ({}): {err}", err.kind()); continue; }, }; + let fclone = f.clone(); thread::spawn(move || { - match f(&mut connection.0) { - Ok(_) => println!("[Port {port}] ({id}) Stream closed."), - Err(err) => println!("[Port {port}] ({id}) {}: {err}", err.kind()), + match fclone(conn) { + Ok(_) => println!("[{name}] ({id}) Stream closed."), + Err(err) => println!("[{name}] ({id}) {}: {err}", err.kind()), } }); } @@ -78,13 +80,13 @@ where /// - `f`: The service handle function. Due to technical difficulties, the function needs to log seperately. pub(crate) fn udp_server(addr: IpAddr, port: u16, name: &str, mut f: F) -> io::Result<()> where - F: FnMut(&mut UdpSocket) -> io::Result<()> + Send + Copy + 'static + F: FnMut(&mut UdpSocket) -> io::Result<()> { // Create UDP Socket let mut socket = match UdpSocket::bind((addr, port)) { Err(err) => return Err(err), Ok(sock) => { - println!("{name} UDP-Server started: {addr}:{port}"); + println!("[{name}] UDP-Server started: {addr}:{port}"); sock } }; @@ -92,7 +94,7 @@ where // Constantly iterate over service function loop { if let Err(err) = f(&mut socket) { - println!("[Port {port}] {}: {err}", err.kind()) + println!("[{name}] {}: {err}", err.kind()) } } } \ No newline at end of file diff --git a/src/time.rs b/src/time.rs index b8c2483..d673f2d 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,16 +1,15 @@ -use std::net::TcpStream; use std::net::IpAddr; use std::io::Write; use std::io; use std::time::SystemTime; use crate::shared::{tcp_server, udp_server, BUFFER_SIZE_UDP}; -pub(crate) const NAME: &str = "Time"; -pub(crate) const PORT: u16 = 37; +const NAME: &str = "Time"; +const PORT: u16 = 37; /// Time TCP-Server -pub(crate) fn time_tcp(addr: IpAddr, port: u16) -> io::Result<()> { - tcp_server(addr, port, NAME, move |stream: &mut TcpStream| { +pub(crate) fn time_tcp(addr: IpAddr) -> io::Result<()> { + tcp_server(addr, PORT, NAME, move |mut stream| { // Send UNIX Timestamp stream.write_all(&unixtime())?; Ok(()) @@ -18,8 +17,8 @@ pub(crate) fn time_tcp(addr: IpAddr, port: u16) -> io::Result<()> { } /// Time UDP-Server -pub(crate) fn time_udp(addr: IpAddr, port: u16) -> io::Result<()> { - udp_server(addr, port, NAME, |socket| { +pub(crate) fn time_udp(addr: IpAddr) -> io::Result<()> { + udp_server(addr, PORT, NAME, |socket| { // Accept Datagram let mut buf = [0; BUFFER_SIZE_UDP]; let (_, peer) = socket.recv_from(&mut buf)?;