Implement hostname resolution
Diff
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(-)
@@ -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"
@@ -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"
@@ -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<SocketAddr>,
resolved_host: Option<String>,
nick: Option<String>,
user: Option<String>,
real_name: Option<String>,
@@ -54,6 +57,7 @@ pub struct ConnectionRequest {
#[derive(Clone, Debug)]
pub struct InitiatedConnection {
pub host: SocketAddr,
pub resolved_host: Option<String>,
pub cloak: String,
pub nick: String,
pub user: String,
@@ -87,6 +91,7 @@ impl TryFrom<ConnectionRequest> for InitiatedConnection {
fn try_from(value: ConnectionRequest) -> Result<Self, Self::Error> {
let ConnectionRequest {
host: Some(host),
resolved_host,
nick: Some(nick),
user: Some(user),
real_name: Some(real_name),
@@ -99,7 +104,8 @@ impl TryFrom<ConnectionRequest> 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<Persistence>,
database: sqlx::Pool<sqlx::Any>,
resolver: &TokioAsyncResolver,
) -> Result<Option<InitiatedConnection>, 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) => {
@@ -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 {
@@ -133,7 +136,7 @@ async fn start_tcp_acceptor_loop(
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");
@@ -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()
)
), ];