diff --git a/Cargo.lock b/Cargo.lock index c9140f0..39cdf91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,9 +221,12 @@ dependencies = [ "basalt-bedrock", "bollard", "clap", + "dialoguer", + "envy", "futures", "lazy_static", "local-ip-address", + "reqwest", "tera", "tokio", "tokio-tar", @@ -575,6 +578,19 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "console" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width 0.2.1", + "windows-sys 0.60.2", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -737,6 +753,18 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" +[[package]] +name = "dialoguer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96" +dependencies = [ + "console", + "shell-words", + "tempfile", + "zeroize", +] + [[package]] name = "digest" version = "0.10.7" @@ -818,6 +846,21 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "enum-ordinalize" version = "4.3.0" @@ -848,6 +891,15 @@ dependencies = [ "url", ] +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1165,6 +1217,25 @@ dependencies = [ "walkdir", ] +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.11.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.6.0" @@ -1300,6 +1371,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -1327,12 +1399,45 @@ dependencies = [ "winapi", ] +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ + "base64", "bytes", "futures-channel", "futures-core", @@ -1340,12 +1445,16 @@ dependencies = [ "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -1747,6 +1856,22 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is_ci" version = "1.2.0" @@ -1934,6 +2059,12 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2675,6 +2806,46 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +[[package]] +name = "reqwest" +version = "0.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "resvg" version = "0.43.0" @@ -2701,6 +2872,20 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "roxmltree" version = "0.20.0" @@ -2742,6 +2927,39 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "rustls" +version = "0.23.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2963,6 +3181,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" @@ -3129,6 +3353,12 @@ dependencies = [ "rustc-hash", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "supports-color" version = "3.0.2" @@ -3202,6 +3432,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -3235,6 +3474,27 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.9.3", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tar" version = "0.4.44" @@ -3471,6 +3731,26 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.17" @@ -3551,6 +3831,45 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.9.3", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3986,6 +4305,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "ureq" version = "2.12.1" @@ -4125,6 +4450,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -4206,6 +4544,16 @@ dependencies = [ "indexmap-nostd", ] +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "weezl" version = "0.1.10" @@ -4284,6 +4632,17 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -4311,6 +4670,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -4671,6 +5039,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerotrie" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 582fc81..cd3da4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,6 @@ tokio-tar = "0.3.1" futures = "0.3.31" local-ip-address = "0.6.5" ansi_term = "0.12.1" +envy = "0.4.2" +dialoguer = "0.12.0" +reqwest = { version = "0.12.23", features = ["json"] } diff --git a/src/auth/login.rs b/src/auth/login.rs new file mode 100644 index 0000000..cd2ba9e --- /dev/null +++ b/src/auth/login.rs @@ -0,0 +1,123 @@ +use std::{collections::HashMap, net::Ipv4Addr, process::ExitStatus}; + +use anyhow::{bail, Context}; +use dialoguer::{theme::ColorfulTheme, Input, Password}; +use tokio::{io, process::Command}; + +use crate::utils::code_to_address; + +pub async fn handle( + code: Option, + addr: Option, + username: Option, + password: Option, +) -> anyhow::Result<()> { + let host: String = if let Some(host) = addr { + format!("http://{}", host) + } else if let Some(code) = code { + // convert game code to IP with port + let (ip, port) = code_to_address(&code).context("Invalid gamecode")?; + format!("http://{}:{}", ip, port) + } else { + bail!("Must provide code or addr") + }; + + let username = username + .context("Username not provided in CLI") + .or(Input::::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter username") + .interact_text() + .context("Failed to obtain username via input from user")) + .context("Failed to determine username")?; + + let password = password + .context("Password not provided in CLI") + .or(Password::with_theme(&ColorfulTheme::default()) + .with_prompt("Enter password") + .with_confirmation("Confirm password", "Passwords do not match") + .interact() + .context("Failed to obtain password via input from user")) + .context("Failed to determine password")?; + + let mut body_map = HashMap::new(); + body_map.insert("username", username); + body_map.insert("password", password); + + let client = reqwest::Client::new(); + let response = client + .post(format!("{}/auth/login", host)) + .json(&body_map) + .send() + .await + .context("Failed to log in")?; + + let session_data = response + .json::>() + .await + .context("Failed to parse response")?; + + let env_overrides = [( + "BASALT_SESSION_TOKEN", + session_data + .get("token") + .context("Response missing token")?, + )]; + + let status = spawn_subshell(env_overrides).await?; + Ok(()) +} + +pub async fn spawn_subshell(overrides: I) -> io::Result +where + I: IntoIterator, + K: Into, + V: Into, +{ + let (path, args) = default_shell_command(); + + let mut cmd = Command::new(path); + cmd.args(args); + + // Use the real TTY (so it acts just like their shell) + cmd.stdin(std::process::Stdio::inherit()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()); + + cmd.envs(overrides.into_iter().map(|(k, v)| (k.into(), v.into()))); + + // If you want to wait async while still responding to signals: + let mut child = cmd.spawn()?; + + // Wait for the shell to exit + let status = child.wait().await?; + Ok(status) +} + +/// Detect default shell + args +#[cfg(unix)] +fn default_shell_command() -> (String, Vec) { + use std::{env, path::Path}; + + let shell_path = env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string()); + let name = Path::new(&shell_path) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("sh"); + + let args: Vec = match name { + "bash" => vec!["-l".into(), "-i".into()], + "zsh" => vec!["-l".into(), "-i".into()], + "fish" => vec!["-l".into(), "-i".into()], + "sh" | "dash" => vec!["-i".into()], + _ => vec!["-i".into()], + }; + + (shell_path, args) +} + +#[cfg(windows)] +fn default_shell_command() -> (String, Vec) { + let comspec = + env::var("COMSPEC").unwrap_or_else(|_| "C:\\Windows\\System32\\cmd.exe".to_string()); + (comspec, vec![]) +} diff --git a/src/auth/mod.rs b/src/auth/mod.rs new file mode 100644 index 0000000..ad044af --- /dev/null +++ b/src/auth/mod.rs @@ -0,0 +1,43 @@ +use std::{net::Ipv4Addr, ops::Sub}; + +use anyhow::{bail, Context}; +use clap::Subcommand; + +mod login; + +#[derive(Clone, Debug, Subcommand, PartialEq, Eq, Hash)] +pub enum SubCmd { + Login { + #[arg(short, long)] + code: Option, + #[arg(long)] + addr: Option, + #[arg(short, long)] + username: Option, + #[arg(short, long)] + password: Option, + }, +} + +impl SubCmd { + /// Validate that the provided arguments are correct + pub fn validate(&self) -> anyhow::Result<()> { + Ok(()) + } +} + +/// Handle the Auth subcommand +pub async fn handle(subcommand: SubCmd) -> anyhow::Result<()> { + subcommand + .validate() + .context("Failed to validate command")?; + match subcommand { + SubCmd::Login { + code, + addr, + username, + password, + } => login::handle(code, addr, username, password).await, + } + .context("Failed to handle auth subcommand") +} diff --git a/src/cli.rs b/src/cli.rs index 9d39ec5..eb5d6cb 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,6 +2,8 @@ use std::{net::Ipv4Addr, path::PathBuf}; use clap::{Parser, Subcommand}; +use crate::auth; + fn default_config() -> &'static std::ffi::OsStr { std::ffi::OsStr::new("basalt.toml") } @@ -78,6 +80,10 @@ pub enum SubCmd { /// determine the IP address ip: Option, }, + Auth { + #[command(subcommand)] + subcommand: auth::SubCmd, + }, } /// CLI tool for generating and running the docker container needed for hosting a basalt diff --git a/src/main.rs b/src/main.rs index a773b12..42436a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ +mod auth; mod build; mod cli; mod init; +mod utils; + use std::{ffi::OsStr, path::Path, process}; use ansi_term::Colour::{Blue, Green}; @@ -11,6 +14,8 @@ use clap::Parser; use cli::Cli; use tokio::fs::File; +use crate::utils::make_game_code; + pub async fn verify(config_file: &Path) -> anyhow::Result<()> { let mut file = File::open(config_file).await?; let res = bedrock::Config::read_async( @@ -32,15 +37,6 @@ pub async fn verify(config_file: &Path) -> anyhow::Result<()> { Ok(()) } -fn make_game_code(bytes: [u8; N]) -> String { - let mut s = String::with_capacity(2 * N); - for b in bytes { - s.push(char::from((b >> 4) + b'a')); - s.push(char::from((b & 0xf) + b'a')); - } - s -} - #[tokio::main] async fn main() -> anyhow::Result<()> { let cli = Cli::parse(); @@ -53,6 +49,7 @@ async fn main() -> anyhow::Result<()> { output, config_file, } => build_with_output(&output, &config_file, tag).await?, + cli::SubCmd::Auth { subcommand } => auth::handle(subcommand).await?, cli::SubCmd::Run { .. } => { todo!(); } diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..9be0499 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,51 @@ +use std::net::Ipv4Addr; + +use anyhow::bail; + +pub fn make_game_code(bytes: [u8; N]) -> String { + let mut s = String::with_capacity(2 * N); + for b in bytes { + s.push(char::from((b >> 4) + b'a')); + s.push(char::from((b & 0xf) + b'a')); + } + s +} + +pub fn code_to_address(code: &str) -> anyhow::Result<(Ipv4Addr, u16)> { + // TODO(Jack): Support RLE + + let mut raw_bytes: Vec = Vec::new(); + for pair in code.as_bytes().chunks(2) { + if let [c1, c2] = pair { + let mut b: u8 = (c1 - b'a') << 4; + b += c2 - b'a'; + raw_bytes.push(b); + } else { + bail!("Invalid gamecode provided") + } + } + + dbg!(raw_bytes.len()); + + let mut x = [0; 6]; + x[..4].copy_from_slice(&raw_bytes[..4]); + x[4..].copy_from_slice(&raw_bytes[4..]); + let ip = Ipv4Addr::new(x[0], x[1], x[2], x[3]); + let port = u16::from_be_bytes([x[4], x[5]]); + Ok((ip, port)) +} + +#[cfg(test)] +mod tests { + use std::net::Ipv4Addr; + + use crate::utils::code_to_address; + + #[test] + fn from_game_code() { + assert_eq!( + code_to_address("makiabecbpja").unwrap(), + (Ipv4Addr::new(192, 168, 1, 66), 8080) + ); + } +}