From 0f4c2633739a7a5911b75539742de3cac3f454ea Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Wed, 31 Jan 2024 02:25:20 +0000 Subject: [PATCH] Implement hostname resolution --- Cargo.lock | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/connection.rs | 21 ++++++++++++++++++++- src/main.rs | 5 ++++- src/server/response.rs | 5 ++++- 5 files changed, 191 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2e91c4..6ce7754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,6 +184,17 @@ dependencies = [ ] [[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] name = "atoi" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -452,6 +463,12 @@ dependencies = [ ] [[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] name = "der" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -554,6 +571,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" [[package]] +name = "enum-as-inner" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -786,6 +815,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] +name = "hickory-proto" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091a6fbccf4860009355e3efc52ff4acf37a63489aad7435372d44ceeb6fbbcf" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b8f021164e6a984c9030023544c57789c51760065cd510572fedcfb04164e8" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", +] + +[[package]] name = "hkdf" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -813,6 +887,17 @@ dependencies = [ ] [[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] name = "humantime" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -846,6 +931,16 @@ dependencies = [ [[package]] name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" @@ -865,6 +960,24 @@ dependencies = [ ] [[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] name = "irc-proto" version = "0.15.0" source = "git+https://github.com/JordanForks/irc#923e080f2408ae301135e82549cff80e92aaa844" @@ -933,6 +1046,12 @@ dependencies = [ ] [[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -955,6 +1074,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1323,6 +1457,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] name = "ring" version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1905,6 +2049,7 @@ dependencies = [ "clap", "const_format", "futures", + "hickory-resolver", "irc-proto", "itertools", "rand", @@ -2141,7 +2286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", ] @@ -2248,6 +2393,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" [[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2420,6 +2571,16 @@ dependencies = [ ] [[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] name = "zerocopy" version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 8ea572b..0bbe178 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ const_format = "0.2" chrono = "0.4" clap = { version = "4.1", features = ["cargo", "derive", "std", "suggestions", "color"] } futures = "0.3" +hickory-resolver = { version = "0.24", features = ["tokio-runtime", "system-config"] } rand = "0.8" serde = { version = "1.0", features = ["derive"] } serde-humantime = "0.1" diff --git a/src/connection.rs b/src/connection.rs index c8cf981..1f59ecd 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -8,6 +8,7 @@ use std::{ io::{Error, ErrorKind}, net::SocketAddr, str::FromStr, + time::Duration, }; use actix::{io::FramedWrite, Actor, Addr}; @@ -15,6 +16,7 @@ use bitflags::bitflags; use chrono::Utc; use const_format::concatcp; use futures::{SinkExt, TryStreamExt}; +use hickory_resolver::TokioAsyncResolver; use irc_proto::{ error::ProtocolError, CapSubCommand, Command, IrcCodec, Message, Prefix, Response, }; @@ -44,6 +46,7 @@ pub struct UserId(pub i64); #[derive(Default)] pub struct ConnectionRequest { host: Option, + resolved_host: Option, nick: Option, user: Option, real_name: Option, @@ -54,6 +57,7 @@ pub struct ConnectionRequest { #[derive(Clone, Debug)] pub struct InitiatedConnection { pub host: SocketAddr, + pub resolved_host: Option, pub cloak: String, pub nick: String, pub user: String, @@ -87,6 +91,7 @@ impl TryFrom for InitiatedConnection { fn try_from(value: ConnectionRequest) -> Result { let ConnectionRequest { host: Some(host), + resolved_host, nick: Some(nick), user: Some(user), real_name: Some(real_name), @@ -99,7 +104,8 @@ impl TryFrom for InitiatedConnection { Ok(Self { host, - cloak: format!("0{}", host.ip()), + resolved_host: resolved_host.clone(), + cloak: resolved_host.unwrap_or_else(|| "xxx".to_string()), nick, user, mode: UserMode::empty(), @@ -121,6 +127,7 @@ pub async fn negotiate_client_connection( host: SocketAddr, persistence: &Addr, database: sqlx::Pool, + resolver: &TokioAsyncResolver, ) -> Result, ProtocolError> { let mut request = ConnectionRequest { host: Some(host), @@ -203,6 +210,18 @@ pub async fn negotiate_client_connection( } }; + if let Ok(Ok(v)) = tokio::time::timeout( + Duration::from_millis(250), + resolver.reverse_lookup(host.ip()), + ) + .await + { + request.resolved_host = v + .iter() + .next() + .map(|v| v.to_utf8().trim_end_matches('.').to_string()); + } + match InitiatedConnection::try_from(std::mem::take(&mut request)) { Ok(v) => break Some(v), Err(v) => { diff --git a/src/main.rs b/src/main.rs index da80296..fc9ef05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use actix_rt::{Arbiter, System}; use bytes::BytesMut; use clap::Parser; use futures::SinkExt; +use hickory_resolver::AsyncResolver; use irc_proto::{Command, IrcCodec, Message}; use rand::seq::SliceRandom; use sqlx::migrate::Migrator; @@ -113,6 +114,7 @@ async fn start_tcp_acceptor_loop( client_threads: usize, ) { let client_arbiters = Arc::new(build_arbiters(client_threads)); + let resolver = Arc::new(AsyncResolver::tokio_from_system_conf().unwrap()); while let Ok((stream, addr)) = listener.accept().await { let span = info_span!("connection", %addr); @@ -124,6 +126,7 @@ async fn start_tcp_acceptor_loop( let server = server.clone(); let client_arbiters = client_arbiters.clone(); let persistence = persistence.clone(); + let resolver = resolver.clone(); actix_rt::spawn(async move { // split the stream into its read and write halves and setup codecs @@ -133,7 +136,7 @@ async fn start_tcp_acceptor_loop( // ensure we have all the details required to actually connect the client to the server // (ie. we have a nick, user, etc) - let connection = match connection::negotiate_client_connection(&mut read, &mut write, addr, &persistence, database).await { + let connection = match connection::negotiate_client_connection(&mut read, &mut write, addr, &persistence, database, &resolver).await { Ok(Some(v)) => v, Ok(None) => { error!("Failed to fully handshake with client, dropping connection"); diff --git a/src/server/response.rs b/src/server/response.rs index f22bbbb..5f1e700 100644 --- a/src/server/response.rs +++ b/src/server/response.rs @@ -90,7 +90,10 @@ impl IntoProtocol for Whois { conn.nick.to_string(), format!( "is connecting from {}@{} {}", - conn.user, conn.host, conn.host + conn.user, + conn.resolved_host + .unwrap_or_else(|| conn.host.ip().to_string()), + conn.host.ip() ) ), // RPL_WHOISHOST ]; -- libgit2 1.7.2