commit 4c8cfaf7c96bda4c8a5bc36ff7492f456bbbd73b Author: Riz Ashraf Date: Thu Oct 8 12:46:38 2026 +0100 feat: implement wait and ping/pong UDP capabilities for pre-push architecture diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..cdfaca8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "udp-send" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..288cbd9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "udp-send" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..987246e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,99 @@ +use std::env; +use std::net::{UdpSocket, SocketAddr, Ipv4Addr}; +use std::process; +use std::time::Duration; + +fn main() { + let mut debug = false; + let mut wait = false; + let mut port_str = String::new(); + let mut payload = String::new(); + let mut positional_args = 0; + + // 1. Iterate args safely, avoiding heavy Vec allocations + for arg in env::args().skip(1) { + if arg == "--debug" { + debug = true; + } else if arg == "--wait" { + wait = true; + } else if positional_args == 0 { + port_str = arg; + positional_args += 1; + } else if positional_args == 1 { + payload = arg; + positional_args += 1; + } + } + + // 2. Fail gracefully or loudly depending on debug flag + if positional_args < 2 { + if debug { + eprintln!("Usage: udp-send [--debug] [--wait] "); + process::exit(1); + } else { + process::exit(0); + } + } + + // 3. Parse port safely + let port: u16 = match port_str.parse() { + Ok(p) => p, + Err(e) => { + if debug { + eprintln!("Error parsing port '{}': {}", port_str, e); + process::exit(1); + } else { + process::exit(0); + } + } + }; + + // 4. Construct addresses + let bind_addr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 0); + let target_addr = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), port); + + // 5. Bind and send, reporting errors if in debug mode + match UdpSocket::bind(bind_addr) { + Ok(socket) => { + if let Err(e) = socket.send_to(payload.as_bytes(), target_addr) { + if debug { + eprintln!("Error sending payload to {}: {}", target_addr, e); + process::exit(1); + } + } else if wait { + // Set a timeout of 10 minutes for waiting + let _ = socket.set_read_timeout(Some(Duration::from_secs(600))); + let mut buf = [0; 1024]; + match socket.recv_from(&mut buf) { + Ok((size, _src)) => { + let response = String::from_utf8_lossy(&buf[..size]); + let resp_trim = response.trim(); + if resp_trim == "APPROVED" || resp_trim == "pong" { + if debug { + println!("Received approval: {}", resp_trim); + } + process::exit(0); + } else { + if debug { + eprintln!("Received denial or unexpected response: {}", resp_trim); + } + process::exit(1); + } + } + Err(e) => { + if debug { + eprintln!("Error waiting for response: {}", e); + } + process::exit(1); + } + } + } + } + Err(e) => { + if debug { + eprintln!("Error binding to local ephemeral port: {}", e); + process::exit(1); + } + } + } +}