From 6b0e15445f18f608820e7b84ec312f6611bb6575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Sun, 25 Aug 2024 23:20:12 +0200 Subject: [PATCH 01/14] Add DHCP server trait skeleton --- src/common/args.rs | 9 +++++ src/servers/dhcp.rs | 80 +++++++++++++++++++++++++++++++++++++++++++ src/servers/mod.rs | 8 +++-- src/servers/server.rs | 23 ++++++++++--- 4 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 src/servers/dhcp.rs diff --git a/src/common/args.rs b/src/common/args.rs index a5373ae..2513dd8 100644 --- a/src/common/args.rs +++ b/src/common/args.rs @@ -65,6 +65,15 @@ pub struct Cli { require_equals = true, value_name = "PORT", )] pub tftp: Option, + + #[arg( + default_missing_value = Protocol::Dhcp.get_default_port().to_string(), + help = format!("Start the DHCP server"), + long, required = false, + num_args = 0, + // require_equals = true, + value_name = "PORT", + )] pub dhcp: Option, } diff --git a/src/servers/dhcp.rs b/src/servers/dhcp.rs new file mode 100644 index 0000000..c749b6f --- /dev/null +++ b/src/servers/dhcp.rs @@ -0,0 +1,80 @@ +use std::collections::HashMap; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::net::{Ipv4Addr, UdpSocket}; +use std::ops::Add; +use std::time::{Duration, Instant}; + + + + +#[async_trait] +pub trait DHCPRunner { + fn new(path: PathBuf, bind_ip: String, port: u16) -> Self; + fn runner(&self); +} + +#[async_trait] +impl DHCPRunner for Server { + fn new(path: PathBuf, bind_ip: String, port: u16) -> Self { + let mut s = Server::default(); + + validation::validate_path(&path).expect("Invalid path"); + validation::validate_ip_port(&bind_ip, port).expect("Invalid bind IP"); + + let path = validation::ensure_trailing_slash(&path); + s.path = Arc::new(path); + s.bind_address = IpAddr::from_str(&bind_ip).expect("Invalid IP address"); + s.port = port; + + s.protocol = Protocol::Dhcp; + DHCPRunner::runner(&s); + s + } + + fn runner(&self) { + let mut receiver = self.sender.subscribe(); + + let bind_address = self.bind_address; + let port = self.port; + let path = self.path.to_string_lossy().to_string(); + + tokio::spawn(async move { + + loop { + debug!("DHCP runner started... Waiting command to connect..."); + let m = receiver.recv().await.unwrap(); + debug!("Message received"); + + if m.connect { + info!("Connecting..."); + // Define new server + // let _ = libundhcp::Server::with_fs(path) + // .passive_ports(50000..65535) + // .metrics() + // .shutdown_indicator(async move { + // loop { + // info!("Connected. Waiting command to disconnect..."); + // let _ = receiver.recv().await.unwrap(); + // break; + // } + // debug!("Gracefully terminating the DHCP server"); + // // Give a few seconds to potential ongoing connections to finish, + // // otherwise finish immediately + // libundhcp::options::Shutdown::new().grace_period(Duration::from_secs(5)) + // }) + // .build() + // .unwrap() + // .listen(format!("{}:{}", bind_address, port)) + // .await.expect("Error starting the HTTP server..."); + break; + } + } + }); + } +} + + + + + diff --git a/src/servers/mod.rs b/src/servers/mod.rs index 4ecdf0a..93970d3 100644 --- a/src/servers/mod.rs +++ b/src/servers/mod.rs @@ -1,11 +1,13 @@ // Re-export the types and items you want to make public from this module. +pub use dhcp::*; pub use ftp::*; -pub use tftp::*; pub use http::*; pub use server::*; +pub use tftp::*; // Import and re-export the submodule files. +pub mod dhcp; pub mod ftp; -pub mod tftp; pub mod http; -pub mod server; \ No newline at end of file +pub mod server; +pub mod tftp; diff --git a/src/servers/server.rs b/src/servers/server.rs index 38c30f6..40c9c7c 100644 --- a/src/servers/server.rs +++ b/src/servers/server.rs @@ -7,31 +7,34 @@ use std::time::Duration; use std::{path::PathBuf, sync::Arc}; use std::net::IpAddr; -use crate::{Cli, CommandMsg, DefaultChannel, FTPRunner, HTTPRunner, TFTPRunner}; +use crate::{Cli, CommandMsg, DefaultChannel, FTPRunner, HTTPRunner, TFTPRunner, DHCPRunner}; #[derive(Debug, Default, PartialEq, Clone)] pub enum Protocol { + Dhcp, + Ftp, #[default] Http, Tftp, - Ftp, } -pub const PROTOCOL_LIST: [&'static Protocol; 3] = [&Protocol::Http, &Protocol::Tftp, &Protocol::Ftp]; +pub const PROTOCOL_LIST: [&'static Protocol; 4] = [&Protocol::Http, &Protocol::Tftp, &Protocol::Ftp, &Protocol::Dhcp]; impl Protocol { pub fn to_string(&self) -> &str { match self { - Protocol::Http => "http", + Protocol::Dhcp => "dhcp", Protocol::Ftp => "ftp", + Protocol::Http => "http", Protocol::Tftp => "tftp", } } pub fn get_default_port(&self) -> u16 { match self { - Protocol::Http => 8080, + Protocol::Dhcp => 0000, Protocol::Ftp => 2121, + Protocol::Http => 8080, Protocol::Tftp => 6969, } } @@ -119,6 +122,9 @@ pub fn server_starter_receiver(channel: &DefaultChannel) { Protocol::Tftp =>{ server = ::new(msg.path.into(), msg.bind_ip, msg.port); }, + Protocol::Dhcp =>{ + server = ::new(msg.path.into(), msg.bind_ip, msg.port); + }, } // Wait the receiver to listen before the sender sends the 1rst msg @@ -176,6 +182,13 @@ pub fn server_starter_sender(cli_args: &Cli, channel: &DefaultChannel Date: Sun, 25 Aug 2024 23:20:59 +0200 Subject: [PATCH 02/14] FTP: Fix log message typo --- src/servers/ftp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/servers/ftp.rs b/src/servers/ftp.rs index 8a4b63e..2932a6b 100644 --- a/src/servers/ftp.rs +++ b/src/servers/ftp.rs @@ -69,7 +69,7 @@ impl FTPRunner for Server { .build() .unwrap() .listen(format!("{}:{}", bind_address, port)) - .await.expect("Error starting the HTTP server..."); + .await.expect("Error starting the FTP server..."); break; } } From 82f3d92735ea42326e02813aafd64c4287fa7814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Sun, 25 Aug 2024 23:38:24 +0200 Subject: [PATCH 03/14] dhcp: add dependency and bump versions --- Cargo.lock | 23 +++++++++++++++++++++++ Cargo.toml | 3 +++ 2 files changed, 26 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f20f004..dbbc220 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1221,6 +1221,17 @@ dependencies = [ "syn 2.0.91", ] +[[package]] +name = "dhcp4r" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afabc97435078b9a514bbdd13726797a7e47ae31a0c09ded1bbf74bc9232bb38" +dependencies = [ + "enum-primitive-derive", + "nom", + "num-traits", +] + [[package]] name = "difflib" version = "0.4.0" @@ -1435,6 +1446,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +[[package]] +name = "enum-primitive-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e" +dependencies = [ + "num-traits", + "quote", + "syn 1.0.109", +] + [[package]] name = "enumflags2" version = "0.7.10" @@ -3312,6 +3334,7 @@ dependencies = [ "bytes", "clap", "ctrlc", + "dhcp4r", "eframe", "egui", "http-body-util", diff --git a/Cargo.toml b/Cargo.toml index 8170538..bfe8b26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,9 @@ http-body-util = "0.1.2" hyper-util = { version = "0.1.10", features = ["full"] } bytes = "1.9.0" +# DHCP server deps +dhcp4r = "0.2.3" + # Log related log = "0.4.22" From a284ca7e79e1abefbb0d6c0df29b2ca0d5a1c4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Sun, 25 Aug 2024 23:39:00 +0200 Subject: [PATCH 04/14] dhcp: add GUI and README info --- README.md | 20 +++++++++++--------- src/ui/window.rs | 7 ++++++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1cd3e99..6f34fd3 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,16 @@ Quick-serve Usage: quick-serve [OPTIONS] Options: - -b, --bind-ip= Bind IP [default: 127.0.0.1] - -p, --serve-dir= Path to serve [default: /tmp/] - -v, --verbose... Verbose logging - -H, --http[=] Start the HTTP server [default port: 8080] - -f, --ftp[=] Start the FTP server [default port: 2121] - -t, --tftp[=] Start the TFTP server [default port: 6969] - -h, --help Print help (see more with '--help') - -V, --version Print version + --headless Headless + -b, --bind-ip= Bind IP [default: 127.0.0.1] + -d, --serve-dir= Directory to serve [default: /tmp/] + -v, --verbose... Verbose logging + --http[=] Start the HTTP server [default port: 8080] + --ftp[=] Start the FTP server [default port: 2121] + --tftp[=] Start the TFTP server [default port: 6969] + --dhcp Start the DHCP server + -h, --help Print help (see more with '--help') + -V, --version Print version ``` @@ -115,7 +117,7 @@ cargo test - [x] HTTP - [x] TFTP - [ ] HTTPS -- [ ] DHCP +- [x] DHCP - [ ] SFTP - [ ] NFS - [ ] SAMBA diff --git a/src/ui/window.rs b/src/ui/window.rs index 94e195c..ccf7139 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -53,9 +53,11 @@ impl eframe::App for UI { egui::widgets::global_theme_preference_switch(ui); + ui.separator(); if ui.button("Exit").clicked() { std::process::exit(0); }; + ui.separator(); }); // ####################################################################### @@ -96,7 +98,10 @@ impl eframe::App for UI { for p in self.protocols.iter_mut() { ui.group(|ui| { ui.add(Label::new(format!("{}", p.protocol.to_string()))); - ui.add(DragValue::new(&mut p.port).range(1..=50000)); + + if p.protocol != Protocol::Dhcp { + ui.add(DragValue::new(&mut p.port).range(1..=50000)); + } if ui.add(toggle(&mut p.start)).clicked() { From 1c7194520f211d1e298c499ddda79324dfef9a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Mon, 30 Dec 2024 13:13:19 +0100 Subject: [PATCH 05/14] Fix building issues --- src/servers/dhcp.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/servers/dhcp.rs b/src/servers/dhcp.rs index c749b6f..013f62e 100644 --- a/src/servers/dhcp.rs +++ b/src/servers/dhcp.rs @@ -1,11 +1,14 @@ -use std::collections::HashMap; -use std::fs::File; -use std::io::{BufRead, BufReader}; -use std::net::{Ipv4Addr, UdpSocket}; -use std::ops::Add; -use std::time::{Duration, Instant}; - - +use async_trait::async_trait; +use std::path::PathBuf; +use super::Server; +use crate::utils::validation; +use std::net::IpAddr; +use std::sync::Arc; +use crate::servers::Protocol; + +use std::str::FromStr; +use log::{debug, info}; +// use std::time::Duration; #[async_trait] From 679a9a1f9bf1445b15f3e9e8ea974edd2cec9a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Mon, 30 Dec 2024 13:13:58 +0100 Subject: [PATCH 06/14] ui: Only shows port number if protocol allows changing it --- src/ui/window.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ui/window.rs b/src/ui/window.rs index ccf7139..0abfc11 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -99,7 +99,10 @@ impl eframe::App for UI { ui.group(|ui| { ui.add(Label::new(format!("{}", p.protocol.to_string()))); - if p.protocol != Protocol::Dhcp { + + // Some protocols do not allow changing ports (and may be set to 0) + // so we only show the port field if it is not 0 + if p.port != 0 { ui.add(DragValue::new(&mut p.port).range(1..=50000)); } From f90644b214bbc910a7297c3aa330e2bef63b619f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Tue, 31 Dec 2024 12:07:37 +0100 Subject: [PATCH 07/14] Introduce DHCP serving tests using docker compose Also: - Remove the test including mainstream dhcp-server - Add timeout --- docker/client.dockerfile | 10 ++++++++++ docker/docker-compose.yml | 25 +++++++++++++++++++++++++ docker/server.dockerfile | 6 ++++++ 3 files changed, 41 insertions(+) create mode 100644 docker/client.dockerfile create mode 100644 docker/docker-compose.yml create mode 100644 docker/server.dockerfile diff --git a/docker/client.dockerfile b/docker/client.dockerfile new file mode 100644 index 0000000..239bef9 --- /dev/null +++ b/docker/client.dockerfile @@ -0,0 +1,10 @@ +# FROM debian:bookworm-slim +FROM ubuntu:latest + +RUN apt-get update && \ + apt-get install -y isc-dhcp-client && \ + rm -rf /var/lib/apt/lists/* + +# For some weird reason, I pass the port 6768 but it opens on 6767 +# so the client also use 6767 +ENTRYPOINT ["/bin/sh", "-c", "timeout 10 dhclient -4 -d -v -p 6768"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..a11f116 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,25 @@ +services: + client: + image: client_image:latest + build: + context: ../ + dockerfile: docker/client.dockerfile + networks: + - c_lan + + server: + image: server_image:latest + build: + context: ../ + dockerfile: docker/server.dockerfile + networks: + c_lan: + ipv4_address: 172.12.1.4 + +networks: + c_lan: + driver: bridge + ipam: + driver: default + config: + - subnet: 172.12.1.0/24 diff --git a/docker/server.dockerfile b/docker/server.dockerfile new file mode 100644 index 0000000..20fd5fa --- /dev/null +++ b/docker/server.dockerfile @@ -0,0 +1,6 @@ +# Use debian:bullseye-slim as the base image +FROM ubuntu:latest + +# Copy the quick-serve binary from the relative path to the container +COPY ./target/debug/quick-serve /usr/local/bin/quick-serve +ENTRYPOINT ["/bin/sh", "-c", "timeout 10 quick-serve --dhcp=6767 -v --bind-ip=172.12.1.4"] From c653949731db8d25136408b9588af1496c348745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Tue, 31 Dec 2024 12:10:55 +0100 Subject: [PATCH 08/14] dhcp: allow passing the port --- src/common/args.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/args.rs b/src/common/args.rs index 2513dd8..409f779 100644 --- a/src/common/args.rs +++ b/src/common/args.rs @@ -70,8 +70,8 @@ pub struct Cli { default_missing_value = Protocol::Dhcp.get_default_port().to_string(), help = format!("Start the DHCP server"), long, required = false, - num_args = 0, - // require_equals = true, + num_args = 0..=1, + require_equals = true, value_name = "PORT", )] pub dhcp: Option, } From 8f647d7eff39fa76726a5d730569d919df597854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Tue, 31 Dec 2024 17:03:37 +0100 Subject: [PATCH 09/14] dhcp: enable port specification --- src/common/args.rs | 4 ++-- src/servers/server.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/args.rs b/src/common/args.rs index 409f779..a8236d6 100644 --- a/src/common/args.rs +++ b/src/common/args.rs @@ -68,12 +68,12 @@ pub struct Cli { #[arg( default_missing_value = Protocol::Dhcp.get_default_port().to_string(), - help = format!("Start the DHCP server"), + help = format!("Start the DHCP server [default port: {}]", Protocol::Dhcp.get_default_port().to_string()), long, required = false, num_args = 0..=1, require_equals = true, value_name = "PORT", - )] pub dhcp: Option, + )] pub dhcp: Option, } diff --git a/src/servers/server.rs b/src/servers/server.rs index 40c9c7c..30a99e9 100644 --- a/src/servers/server.rs +++ b/src/servers/server.rs @@ -32,7 +32,7 @@ impl Protocol { } pub fn get_default_port(&self) -> u16 { match self { - Protocol::Dhcp => 0000, + Protocol::Dhcp => 67, Protocol::Ftp => 2121, Protocol::Http => 8080, Protocol::Tftp => 6969, From ba03696c83e5a7104cf62a11ae0423913eb22cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Tue, 31 Dec 2024 17:05:18 +0100 Subject: [PATCH 10/14] dhcp: introduce working implementation for the server --- src/servers/dhcp.rs | 47 +++---- src/servers/dhcp_server/dhcp_server.rs | 172 +++++++++++++++++++++++++ src/servers/dhcp_server/mod.rs | 3 + src/servers/mod.rs | 1 + src/servers/server.rs | 2 +- 5 files changed, 197 insertions(+), 28 deletions(-) create mode 100644 src/servers/dhcp_server/dhcp_server.rs create mode 100644 src/servers/dhcp_server/mod.rs diff --git a/src/servers/dhcp.rs b/src/servers/dhcp.rs index 013f62e..0dd91de 100644 --- a/src/servers/dhcp.rs +++ b/src/servers/dhcp.rs @@ -2,14 +2,16 @@ use async_trait::async_trait; use std::path::PathBuf; use super::Server; use crate::utils::validation; -use std::net::IpAddr; +use std::net::{IpAddr, Ipv4Addr}; use std::sync::Arc; use crate::servers::Protocol; use std::str::FromStr; -use log::{debug, info}; -// use std::time::Duration; +use log::debug; +use std::net::UdpSocket; +use dhcp4r::server as dhcp_server; +use crate::servers::dhcp_server::DhcpServer; #[async_trait] pub trait DHCPRunner { @@ -22,7 +24,6 @@ impl DHCPRunner for Server { fn new(path: PathBuf, bind_ip: String, port: u16) -> Self { let mut s = Server::default(); - validation::validate_path(&path).expect("Invalid path"); validation::validate_ip_port(&bind_ip, port).expect("Invalid bind IP"); let path = validation::ensure_trailing_slash(&path); @@ -40,36 +41,28 @@ impl DHCPRunner for Server { let bind_address = self.bind_address; let port = self.port; - let path = self.path.to_string_lossy().to_string(); + let ip_port = format!("{}:{}", bind_address, port); + let socket_bind = format!("0.0.0.0:{}", port); tokio::spawn(async move { - loop { debug!("DHCP runner started... Waiting command to connect..."); let m = receiver.recv().await.unwrap(); debug!("Message received"); - + if m.connect { - info!("Connecting..."); - // Define new server - // let _ = libundhcp::Server::with_fs(path) - // .passive_ports(50000..65535) - // .metrics() - // .shutdown_indicator(async move { - // loop { - // info!("Connected. Waiting command to disconnect..."); - // let _ = receiver.recv().await.unwrap(); - // break; - // } - // debug!("Gracefully terminating the DHCP server"); - // // Give a few seconds to potential ongoing connections to finish, - // // otherwise finish immediately - // libundhcp::options::Shutdown::new().grace_period(Duration::from_secs(5)) - // }) - // .build() - // .unwrap() - // .listen(format!("{}:{}", bind_address, port)) - // .await.expect("Error starting the HTTP server..."); + + debug!("DHCP server started on {}", ip_port); + + let ms = DhcpServer::default(); + + let socket = UdpSocket::bind(socket_bind.clone()).unwrap(); + socket.set_broadcast(true).unwrap(); + + let ipv4: Ipv4Addr = bind_address.clone().to_string().parse().unwrap(); + dhcp_server::Server::serve(socket, ipv4, ms); + + debug!("DHCP server stopped"); break; } } diff --git a/src/servers/dhcp_server/dhcp_server.rs b/src/servers/dhcp_server/dhcp_server.rs new file mode 100644 index 0000000..e3fde42 --- /dev/null +++ b/src/servers/dhcp_server/dhcp_server.rs @@ -0,0 +1,172 @@ +use dhcp4r::{options, packet, server}; +use dhcp4r::bytes_u32; + +use std::collections::HashMap; +use std::net::Ipv4Addr; +use std::time::{Duration, Instant}; +use std::ops::Add; +use log::{debug, info}; + + +// Server configuration +const LEASE_DURATION_SECS: u32 = 7200; +const IP_START: [u8; 4] = [172, 12, 1, 100]; +const ROUTER_IP: Ipv4Addr = Ipv4Addr::new(172, 12, 1, 254); +const SUBNET_MASK: Ipv4Addr = Ipv4Addr::new(255, 255, 255, 0); +const DNS_IPS: [Ipv4Addr; 2] = [ + // Google DNS servers + Ipv4Addr::new(8, 8, 8, 8), + Ipv4Addr::new(4, 4, 4, 4), +]; +const LEASE_NUM: u32 = 100; + +// Derived constants +const IP_START_NUM: u32 = bytes_u32!(IP_START); + + + +pub struct DhcpServer { + pub leases: HashMap, + pub last_lease: u32, +} + +impl Default for DhcpServer { + fn default() -> Self { + DhcpServer { + leases: HashMap::new(), + last_lease: 0, + } + } +} + + +impl server::Handler for DhcpServer { + fn handle_request(&mut self, server: &server::Server, in_packet: packet::Packet) { + + debug!("Request received"); + + match in_packet.message_type() { + Ok(options::MessageType::Discover) => { + // Prefer client's choice if available + if let Some(options::DhcpOption::RequestedIpAddress(addr)) = + in_packet.option(options::REQUESTED_IP_ADDRESS) + { + let addr = *addr; + if self.available(&in_packet.chaddr, &addr) { + reply(server, options::MessageType::Offer, in_packet, &addr); + return; + } + } + // Otherwise prefer existing (including expired if available) + if let Some(ip) = self.current_lease(&in_packet.chaddr) { + reply(server, options::MessageType::Offer, in_packet, &ip); + return; + } + // Otherwise choose a free ip if available + for _ in 0..LEASE_NUM { + self.last_lease = (self.last_lease + 1) % LEASE_NUM; + if self.available( + &in_packet.chaddr, + &((IP_START_NUM + &self.last_lease).into()), + ) { + reply( + server, + options::MessageType::Offer, + in_packet, + &((IP_START_NUM + &self.last_lease).into()), + ); + break; + } + } + } + + Ok(options::MessageType::Request) => { + // Ignore requests to alternative DHCP server + if !server.for_this_server(&in_packet) { + return; + } + let req_ip = match in_packet.option(options::REQUESTED_IP_ADDRESS) { + Some(options::DhcpOption::RequestedIpAddress(x)) => *x, + _ => in_packet.ciaddr, + }; + if !&self.available(&in_packet.chaddr, &req_ip) { + nak(server, in_packet, "Requested IP not available"); + return; + } + self.leases.insert( + req_ip, + (in_packet.chaddr, Instant::now().add( + Duration::new(LEASE_DURATION_SECS as u64, 0)) + ), + ); + reply(server, options::MessageType::Ack, in_packet, &req_ip); + } + + Ok(options::MessageType::Release) | Ok(options::MessageType::Decline) => { + // Ignore requests to alternative DHCP server + if !server.for_this_server(&in_packet) { + return; + } + if let Some(ip) = self.current_lease(&in_packet.chaddr) { + self.leases.remove(&ip); + } + } + + // TODO - not necessary but support for dhcp4r::INFORM might be nice + _ => {} + } + } +} + +impl DhcpServer { + fn available(&self, chaddr: &[u8; 6], addr: &Ipv4Addr) -> bool { + let pos: u32 = (*addr).into(); + pos >= IP_START_NUM + && pos < IP_START_NUM + LEASE_NUM + && match self.leases.get(addr) { + Some(x) => x.0 == *chaddr || Instant::now().gt(&x.1), + None => true, + } + } + + fn current_lease(&self, chaddr: &[u8; 6]) -> Option { + for (i, v) in &self.leases { + if &v.0 == chaddr { + return Some(*i); + } + } + return None; + } +} + +fn reply( + s: &server::Server, + msg_type: options::MessageType, + req_packet: packet::Packet, + offer_ip: &Ipv4Addr, +) { + let _ = s.reply( + msg_type, + vec![ + options::DhcpOption::IpAddressLeaseTime(LEASE_DURATION_SECS), + options::DhcpOption::SubnetMask(SUBNET_MASK), + options::DhcpOption::Router(vec![ROUTER_IP]), + options::DhcpOption::DomainNameServer(DNS_IPS.to_vec()), + ], + *offer_ip, + req_packet, + ); + info!("offered {:?}", offer_ip); +} + +fn nak(s: &server::Server, req_packet: packet::Packet, message: &str) { + let _ = s.reply( + options::MessageType::Nak, + vec![options::DhcpOption::Message(message.to_string())], + Ipv4Addr::new(0, 0, 0, 0), + req_packet, + ); +} + + + diff --git a/src/servers/dhcp_server/mod.rs b/src/servers/dhcp_server/mod.rs new file mode 100644 index 0000000..1c7675f --- /dev/null +++ b/src/servers/dhcp_server/mod.rs @@ -0,0 +1,3 @@ +pub use dhcp_server::*; + +pub mod dhcp_server; diff --git a/src/servers/mod.rs b/src/servers/mod.rs index 93970d3..160fd07 100644 --- a/src/servers/mod.rs +++ b/src/servers/mod.rs @@ -7,6 +7,7 @@ pub use tftp::*; // Import and re-export the submodule files. pub mod dhcp; +pub mod dhcp_server; pub mod ftp; pub mod http; pub mod server; diff --git a/src/servers/server.rs b/src/servers/server.rs index 30a99e9..91506ff 100644 --- a/src/servers/server.rs +++ b/src/servers/server.rs @@ -32,7 +32,7 @@ impl Protocol { } pub fn get_default_port(&self) -> u16 { match self { - Protocol::Dhcp => 67, + Protocol::Dhcp => 6767, Protocol::Ftp => 2121, Protocol::Http => 8080, Protocol::Tftp => 6969, From fb33d25c4ceba77bd520927ffd5ebf7c25ca1bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Thu, 2 Jan 2025 09:46:20 +0100 Subject: [PATCH 11/14] Add testcontainers for performing the dockerized tests --- Cargo.lock | 721 +++++++++++++++++++++++++++++++++++++++----- Cargo.toml | 6 +- src/servers/dhcp.rs | 142 +++++++++ 3 files changed, 793 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dbbc220..584115f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -338,7 +338,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "synstructure", ] @@ -350,7 +350,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -371,9 +371,9 @@ dependencies = [ [[package]] name = "async-broadcast" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ "event-listener 5.3.1", "event-listener-strategy", @@ -514,7 +514,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -569,7 +569,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -648,16 +648,15 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8478a5c29ead3f3be14aff8a202ad965cf7da6856860041bfca271becf8ba48b" +checksum = "923ded50f602b3007e5e63e3f094c479d9c8a9b42d7f4034e4afe456aa48bfd2" dependencies = [ "bindgen", "cc", "cmake", "dunce", "fs_extra", - "libc", "paste", ] @@ -676,6 +675,18 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bindgen" version = "0.69.5" @@ -695,7 +706,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.91", + "syn 2.0.94", "which", ] @@ -763,14 +774,64 @@ dependencies = [ "piper", ] +[[package]] +name = "bollard" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41711ad46fda47cd701f6908e59d1bd6b9a2b7464c0d0aeab95c6d37096ff8a" +dependencies = [ + "base64 0.22.1", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "home", + "http", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-rustls", + "hyper-util", + "hyperlocal", + "log", + "pin-project-lite", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.45.0-rc.26.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7c5415e3a6bc6d3e99eff6268e488fd4ee25e7b28c10f08fa6760bd9de16e4" +dependencies = [ + "serde", + "serde_repr", + "serde_with", +] + [[package]] name = "bstr" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.9", "serde", ] @@ -797,7 +858,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -875,9 +936,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.5" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" dependencies = [ "jobserver", "libc", @@ -935,6 +996,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", + "serde", "windows-targets 0.52.6", ] @@ -980,7 +1042,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -1166,6 +1228,41 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.94", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.94", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1206,6 +1303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1218,7 +1316,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -1262,7 +1360,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -1280,6 +1378,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "docker_credential" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31951f49556e34d90ed28342e1df7e1cb7a229c4cab0aecc627b5d91edd41d07" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "document-features" version = "0.2.10" @@ -1475,7 +1584,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -1524,6 +1633,17 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -1575,6 +1695,18 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "flate2" version = "1.0.35" @@ -1624,7 +1756,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -1667,6 +1799,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -1689,6 +1822,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -1731,7 +1875,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -1764,6 +1908,19 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +dependencies = [ + "cfg-if", + "libc", + "log", + "rustversion", + "windows", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1814,9 +1971,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "glow" @@ -1959,13 +2116,19 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.5" @@ -2087,6 +2250,38 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.10" @@ -2106,6 +2301,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyperlocal" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" +dependencies = [ + "hex", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -2244,9 +2454,15 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -2289,6 +2505,17 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.7.0" @@ -2297,6 +2524,7 @@ checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", + "serde", ] [[package]] @@ -2534,6 +2762,19 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -2543,6 +2784,15 @@ dependencies = [ "libc", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "maybe-owned" version = "0.3.4" @@ -2627,20 +2877,19 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" +checksum = "23db87a7f248211f6a7c8644a1b750541f8a4c68ae7de0f908860e44c0c201f6" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "once_cell", + "loom", "parking_lot", "rustc_version", "smallvec", "tagptr", "thiserror", - "triomphe", "uuid", ] @@ -2656,7 +2905,7 @@ dependencies = [ "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", - "indexmap", + "indexmap 2.7.0", "log", "rustc-hash", "spirv", @@ -2739,6 +2988,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2802,7 +3061,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -3041,6 +3300,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "orbclient" version = "0.3.48" @@ -3060,6 +3325,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owned_ttf_parser" version = "0.25.0" @@ -3098,6 +3369,31 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "parse-display" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +dependencies = [ + "parse-display-derive", + "regex", + "regex-syntax 0.8.5", +] + +[[package]] +name = "parse-display-derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "regex-syntax 0.8.5", + "structmeta", + "syn 2.0.94", +] + [[package]] name = "paste" version = "1.0.15" @@ -3139,7 +3435,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -3273,7 +3569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -3347,6 +3643,8 @@ dependencies = [ "rfd", "sha2", "tempfile", + "testcontainers", + "testcontainers-modules", "tokio", "unftp-sbe-fs", ] @@ -3372,9 +3670,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -3415,6 +3713,15 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -3441,8 +3748,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -3453,9 +3769,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -3573,12 +3895,26 @@ dependencies = [ "aws-lc-rs", "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -3606,6 +3942,18 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "same-file" version = "1.0.6" @@ -3615,6 +3963,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3640,6 +3997,29 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.24" @@ -3648,22 +4028,34 @@ checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" [[package]] name = "serde" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", +] + +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", ] [[package]] @@ -3674,7 +4066,49 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.94", ] [[package]] @@ -3699,6 +4133,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -3909,6 +4352,29 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "structmeta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn 2.0.94", +] + +[[package]] +name = "structmeta-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.94", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3928,9 +4394,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.91" +version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" dependencies = [ "proc-macro2", "quote", @@ -3945,7 +4411,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -3956,12 +4422,13 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand 2.3.0", + "getrandom", "once_cell", "rustix 0.38.42", "windows-sys 0.59.0", @@ -3982,6 +4449,44 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" +[[package]] +name = "testcontainers" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f40cc2bd72e17f328faf8ca7687fe337e61bccd8acf9674fa78dd3792b045e1" +dependencies = [ + "async-trait", + "bollard", + "bollard-stubs", + "bytes", + "docker_credential", + "either", + "etcetera", + "futures", + "log", + "memchr", + "parse-display", + "pin-project-lite", + "serde", + "serde_json", + "serde_with", + "thiserror", + "tokio", + "tokio-stream", + "tokio-tar", + "tokio-util", + "url", +] + +[[package]] +name = "testcontainers-modules" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064a2677e164cad39ef3c1abddb044d5a25c49d27005804563d8c4227aac8bd0" +dependencies = [ + "testcontainers", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -3999,7 +4504,17 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", ] [[package]] @@ -4094,7 +4609,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -4118,6 +4633,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tar" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75" +dependencies = [ + "filetime", + "futures-core", + "libc", + "redox_syscall 0.3.5", + "tokio", + "tokio-stream", + "xattr", +] + [[package]] name = "tokio-util" version = "0.7.13" @@ -4143,7 +4673,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap", + "indexmap 2.7.0", "toml_datetime", "winnow", ] @@ -4173,7 +4703,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -4183,13 +4713,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", ] [[package]] -name = "triomphe" -version = "0.1.11" +name = "tracing-subscriber" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] [[package]] name = "try-lock" @@ -4323,6 +4877,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.5" @@ -4390,7 +4950,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "wasm-bindgen-shared", ] @@ -4425,7 +4985,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4618,7 +5178,7 @@ dependencies = [ "bitflags 2.6.0", "cfg_aliases 0.1.1", "document-features", - "indexmap", + "indexmap 2.7.0", "log", "naga", "once_cell", @@ -4766,7 +5326,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -4777,7 +5337,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -5067,9 +5627,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "e6f5bb5257f2407a5425c6e749bfd9692192a73e70a6060516ac04f889087d68" dependencies = [ "memchr", ] @@ -5145,6 +5705,17 @@ dependencies = [ "time", ] +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys 0.4.14", + "rustix 0.38.42", +] + [[package]] name = "xcursor" version = "0.3.8" @@ -5206,7 +5777,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "synstructure", ] @@ -5302,7 +5873,7 @@ checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "zbus-lockstep", "zbus_xml", "zvariant 4.2.0", @@ -5317,7 +5888,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "zvariant_utils 2.1.0", ] @@ -5330,7 +5901,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "zbus_names 4.1.0", "zvariant 5.1.0", "zvariant_utils 3.0.2", @@ -5390,7 +5961,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -5410,7 +5981,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "synstructure", ] @@ -5439,7 +6010,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -5480,7 +6051,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "zvariant_utils 2.1.0", ] @@ -5493,7 +6064,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", "zvariant_utils 3.0.2", ] @@ -5505,7 +6076,7 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.91", + "syn 2.0.94", ] [[package]] @@ -5518,6 +6089,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.91", + "syn 2.0.94", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index bfe8b26..2964f83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,8 +58,12 @@ eframe = {version = "0.30.0", features = ["wayland"], optional = true} rfd = {version = "0.15.1", optional = true} [dev-dependencies] + +testcontainers = { version = "0.23.1" } +testcontainers-modules = { version = "0.11.4", features = ["blocking"] } + sha2 = "0.10.8" -tempfile = "3.14.0" +tempfile = "3.15.0" rand = "0.8.5" assert_cmd = "2.0.16" predicates = "3.1.3" diff --git a/src/servers/dhcp.rs b/src/servers/dhcp.rs index 0dd91de..4ce63b7 100644 --- a/src/servers/dhcp.rs +++ b/src/servers/dhcp.rs @@ -74,3 +74,145 @@ impl DHCPRunner for Server { + + +#[cfg(test)] +mod tests { + + // use testcontainers_modules::{postgres, testcontainers::runners::SyncRunner}; + + // use crate::tests::common::tests::*; + use std::io::{stdout, BufRead}; + use crate::servers::Protocol; + use testcontainers::{core::{IntoContainerPort, WaitFor}, runners::SyncRunner, GenericImage, ImageExt}; + + // #[test] + // fn e2e() { + // let proto = Protocol::Dhcp; + // let port = 2223u16; + // } + + + #[test] + fn client() { + + println!("Client test"); + + // let image = ImageExt::with_cmd(self, cmd) + // let docker = clients::Cli::default(); + let custom_image = GenericImage::new("client_image", "latest"); + + // let custom_image = custom_image + // .with_env_var("DEBUG", "1") + // .with_cmd(vec!["sleep", "5"]); + + let container = custom_image.start().unwrap(); + + + let stderr = container.stderr(true); + // let stdout = container.stdout(true); + + // it's possible to send logs to another thread + let log_follower_thread = std::thread::spawn(move || { + // let stdout_lines = stdout.lines(); + // for line in stdout_lines { + // println!("stdout: {}", line.unwrap()); + // } + + let mut std_lines = stderr.lines(); + let expected_messages = [ + "binding to user-specified port", + ]; + for expected_message in expected_messages { + let line = std_lines.next().expect("line must exist")?; + if !line.contains(expected_message) { + println!("Log message ('{}') doesn't contain expected message ('{}')", line, expected_message); + anyhow::bail!( + "Log message ('{}') doesn't contain expected message ('{}')", + line, + expected_message + ); + } + } + Ok(()) + }); + + + // let expected_messages = [ + // "binding to user-specified port", + // "dadasdasdasdasd", + // ]; + + // let mut stdout_lines = stdout.lines(); + // for expected_message in expected_messages { + // let line = stdout_lines.next().expect("line must exist").unwrap(); + // if !line.contains(expected_message) { + // println!("Log message ('{}') doesn't contain expected message ('{}')", line, expected_message); + // } + // } + + let _ = log_follower_thread + .join().unwrap_or_else(|_| Err(anyhow::anyhow!("failed to join log follower thread"))); + + // logs are accessible after container is stopped + let _ = container.stop(); + + + let stdout = String::from_utf8(container.stdout_to_vec().unwrap()).unwrap(); + + println!("*************stdout:\n\n{}", stdout); + + } + + // #[test] + // fn sync_logs_are_accessible() -> anyhow::Result<()> { + // let image = GenericImage::new("testcontainers/helloworld", "1.1.0"); + // let container = image.start()?; + + // let stderr = container.stderr(true); + + // // it's possible to send logs to another thread + // let log_follower_thread = std::thread::spawn(move || { + // let mut stderr_lines = stderr.lines(); + // let expected_messages = [ + // "DELAY_START_MSEC: 0", + // "Sleeping for 0 ms", + // "Starting server on port 8080", + // "Sleeping for 0 ms", + // "Starting server on port 8081", + // "Ready, listening on 8080 and 8081", + // ]; + // for expected_message in expected_messages { + // let line = stderr_lines.next().expect("line must exist")?; + // if !line.contains(expected_message) { + // anyhow::bail!( + // "Log message ('{}') doesn't contain expected message ('{}')", + // line, + // expected_message + // ); + // } + // } + // Ok(()) + // }); + // log_follower_thread + // .join() + // .map_err(|_| anyhow::anyhow!("failed to join log follower thread"))??; + + // // logs are accessible after container is stopped + // container.stop()?; + + // // stdout is empty + // let stdout = String::from_utf8(container.stdout_to_vec()?)?; + // assert_eq!(stdout, ""); + // // stderr contains 6 lines + // let stderr = String::from_utf8(container.stderr_to_vec()?)?; + // assert_eq!( + // stderr.lines().count(), + // 6, + // "unexpected stderr size: {}", + // stderr + // ); + // Ok(()) + // } + +} From 0698ff60d96b73cc68b76b9c720be0f7522b068a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Thu, 2 Jan 2025 17:56:10 +0100 Subject: [PATCH 12/14] dhcp: server working with dockerized tests --- src/servers/dhcp.rs | 177 +++++++++++++------------------------------- 1 file changed, 51 insertions(+), 126 deletions(-) diff --git a/src/servers/dhcp.rs b/src/servers/dhcp.rs index 4ce63b7..441002e 100644 --- a/src/servers/dhcp.rs +++ b/src/servers/dhcp.rs @@ -49,18 +49,17 @@ impl DHCPRunner for Server { debug!("DHCP runner started... Waiting command to connect..."); let m = receiver.recv().await.unwrap(); debug!("Message received"); - + if m.connect { - debug!("DHCP server started on {}", ip_port); - let ms = DhcpServer::default(); + let server = DhcpServer::default(); let socket = UdpSocket::bind(socket_bind.clone()).unwrap(); socket.set_broadcast(true).unwrap(); let ipv4: Ipv4Addr = bind_address.clone().to_string().parse().unwrap(); - dhcp_server::Server::serve(socket, ipv4, ms); + dhcp_server::Server::serve(socket, ipv4, server); debug!("DHCP server stopped"); break; @@ -71,148 +70,74 @@ impl DHCPRunner for Server { } - - - - - #[cfg(test)] mod tests { + use testcontainers::{runners::SyncRunner, GenericImage}; + use std::sync::Once; + use std::process::Command; - // use testcontainers_modules::{postgres, testcontainers::runners::SyncRunner}; - // use crate::tests::common::tests::*; - use std::io::{stdout, BufRead}; - use crate::servers::Protocol; - use testcontainers::{core::{IntoContainerPort, WaitFor}, runners::SyncRunner, GenericImage, ImageExt}; + static INIT: Once = Once::new(); + fn build_images() { + INIT.call_once(|| { + // Create the docker images here - // #[test] - // fn e2e() { - // let proto = Protocol::Dhcp; - // let port = 2223u16; - // } + let dir = std::env::current_dir().unwrap().join("docker"); + let _out = Command::new("docker") + .arg("compose") + .arg("build") + .current_dir(dir) + .output() + .expect("Failed to execute command"); + // println!("{}", String::from_utf8_lossy(&output.stdout)); + }); + } #[test] - fn client() { - - println!("Client test"); - - // let image = ImageExt::with_cmd(self, cmd) - // let docker = clients::Cli::default(); - let custom_image = GenericImage::new("client_image", "latest"); - - // let custom_image = custom_image - // .with_env_var("DEBUG", "1") - // .with_cmd(vec!["sleep", "5"]); - - let container = custom_image.start().unwrap(); + fn ip_assigning() { + build_images(); + let client_thread = std::thread::spawn(move || { + let custom_image = GenericImage::new("client_image", "latest"); + let container = custom_image.start().unwrap(); + let _ = container.stop(); - let stderr = container.stderr(true); - // let stdout = container.stdout(true); + let out = String::from_utf8(container.stderr_to_vec().unwrap()).unwrap(); - // it's possible to send logs to another thread - let log_follower_thread = std::thread::spawn(move || { - // let stdout_lines = stdout.lines(); - // for line in stdout_lines { - // println!("stdout: {}", line.unwrap()); - // } - - let mut std_lines = stderr.lines(); - let expected_messages = [ + let expected_lines = [ "binding to user-specified port", + "DHCPDISCOVER on", + "bound to 172.12.1.101", ]; - for expected_message in expected_messages { - let line = std_lines.next().expect("line must exist")?; - if !line.contains(expected_message) { - println!("Log message ('{}') doesn't contain expected message ('{}')", line, expected_message); - anyhow::bail!( - "Log message ('{}') doesn't contain expected message ('{}')", - line, - expected_message - ); - } + + for expected in &expected_lines { + assert!(out.contains(expected), + "Expected line not found: {}\nCheck on the complete logs:\n{}", expected, out); } - Ok(()) }); - // let expected_messages = [ - // "binding to user-specified port", - // "dadasdasdasdasd", - // ]; - - // let mut stdout_lines = stdout.lines(); - // for expected_message in expected_messages { - // let line = stdout_lines.next().expect("line must exist").unwrap(); - // if !line.contains(expected_message) { - // println!("Log message ('{}') doesn't contain expected message ('{}')", line, expected_message); - // } - // } - - let _ = log_follower_thread - .join().unwrap_or_else(|_| Err(anyhow::anyhow!("failed to join log follower thread"))); - - // logs are accessible after container is stopped - let _ = container.stop(); + let server_thread = std::thread::spawn(move || { + let custom_image = GenericImage::new("server_image", "latest"); + let container = custom_image.start().unwrap(); + let _ = container.stop(); + let out = String::from_utf8(container.stdout_to_vec().unwrap()).unwrap(); - let stdout = String::from_utf8(container.stdout_to_vec().unwrap()).unwrap(); + let expected_lines = [ + "DHCP server started", + "dhcp_server: Request received", + "dhcp_server: offered 172.12.1.101", + ]; - println!("*************stdout:\n\n{}", stdout); + for expected in &expected_lines { + assert!(out.contains(expected), + "Expected line not found: {}\nCheck on the complete logs:\n{}", expected, out); + } + }); + client_thread.join().unwrap(); + server_thread.join().unwrap(); } - - // #[test] - // fn sync_logs_are_accessible() -> anyhow::Result<()> { - // let image = GenericImage::new("testcontainers/helloworld", "1.1.0"); - // let container = image.start()?; - - // let stderr = container.stderr(true); - - // // it's possible to send logs to another thread - // let log_follower_thread = std::thread::spawn(move || { - // let mut stderr_lines = stderr.lines(); - // let expected_messages = [ - // "DELAY_START_MSEC: 0", - // "Sleeping for 0 ms", - // "Starting server on port 8080", - // "Sleeping for 0 ms", - // "Starting server on port 8081", - // "Ready, listening on 8080 and 8081", - // ]; - // for expected_message in expected_messages { - // let line = stderr_lines.next().expect("line must exist")?; - // if !line.contains(expected_message) { - // anyhow::bail!( - // "Log message ('{}') doesn't contain expected message ('{}')", - // line, - // expected_message - // ); - // } - // } - // Ok(()) - // }); - // log_follower_thread - // .join() - // .map_err(|_| anyhow::anyhow!("failed to join log follower thread"))??; - - // // logs are accessible after container is stopped - // container.stop()?; - - // // stdout is empty - // let stdout = String::from_utf8(container.stdout_to_vec()?)?; - // assert_eq!(stdout, ""); - // // stderr contains 6 lines - // let stderr = String::from_utf8(container.stderr_to_vec()?)?; - // assert_eq!( - // stderr.lines().count(), - // 6, - // "unexpected stderr size: {}", - // stderr - // ); - // Ok(()) - // } - } From 8770f3d5095735840893bab0c81edd8d05086218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Thu, 2 Jan 2025 22:11:39 +0100 Subject: [PATCH 13/14] Add docker check step to tests --- src/servers/dhcp.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/servers/dhcp.rs b/src/servers/dhcp.rs index 441002e..0c0c401 100644 --- a/src/servers/dhcp.rs +++ b/src/servers/dhcp.rs @@ -73,25 +73,26 @@ impl DHCPRunner for Server { #[cfg(test)] mod tests { use testcontainers::{runners::SyncRunner, GenericImage}; - use std::sync::Once; + use std::env; use std::process::Command; + // use std::sync::Once; - static INIT: Once = Once::new(); + // static INIT: Once = Once::new(); fn build_images() { - INIT.call_once(|| { + // INIT.call_once(|| { // Create the docker images here + let cwd = env::var("CARGO_MANIFEST_DIR").unwrap(); - let dir = std::env::current_dir().unwrap().join("docker"); let _out = Command::new("docker") .arg("compose") .arg("build") - .current_dir(dir) + .current_dir(format!("{cwd}/docker/")) .output() - .expect("Failed to execute command"); + .expect(&format!("Failed to execute command. Check directory {}", cwd)); - // println!("{}", String::from_utf8_lossy(&output.stdout)); - }); + println!("{}", String::from_utf8_lossy(&_out.stdout)); + // }); } #[test] From 3ac181694ca7801a4e5e05ff8bc64cfe290df8d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Loureiro?= Date: Thu, 2 Jan 2025 22:12:30 +0100 Subject: [PATCH 14/14] Install docker compose to pipeline --- .github/workflows/rust.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c1cb495..5772229 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -57,12 +57,13 @@ jobs: profile: minimal toolchain: stable - - name: Install extra packages + - name: Install Dependencies run: | - cargo install cross --git https://github.com/cross-rs/cross + sudo apt-get update + sudo apt-get install -y docker-compose nasm build-essential clang wget tftp-hpa curl - - name: Test on target + - name: Test continue-on-error: false run: | - cross build --release - cross test --release + cargo build + cargo test -- --nocapture