mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'dequbed/srv-lookup' into 'master'
Add SRV lookup functionality to voxygen See merge request veloren/veloren!4310
This commit is contained in:
commit
ba0fc08f8d
143
Cargo.lock
generated
143
Cargo.lock
generated
@ -1718,7 +1718,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
"winreg",
|
||||
"winreg 0.51.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1847,6 +1847,18 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2 1.0.78",
|
||||
"quote 1.0.35",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator"
|
||||
version = "0.7.0"
|
||||
@ -2750,6 +2762,51 @@ dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[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 1.0.0",
|
||||
"data-encoding",
|
||||
"enum-as-inner",
|
||||
"futures-channel",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"idna 0.4.0",
|
||||
"ipnet",
|
||||
"once_cell",
|
||||
"rand 0.8.5",
|
||||
"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 1.0.0",
|
||||
"futures-util",
|
||||
"hickory-proto",
|
||||
"ipconfig",
|
||||
"lru-cache",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.1",
|
||||
"rand 0.8.5",
|
||||
"resolv-conf",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
@ -2759,6 +2816,17 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[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 = "http"
|
||||
version = "0.2.11"
|
||||
@ -2937,6 +3005,16 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[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"
|
||||
@ -3070,6 +3148,24 @@ dependencies = [
|
||||
"mach2",
|
||||
]
|
||||
|
||||
[[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 0.50.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.10"
|
||||
@ -3399,6 +3495,15 @@ dependencies = [
|
||||
"hashbrown 0.14.3",
|
||||
]
|
||||
|
||||
[[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 = "lz-fear"
|
||||
version = "0.1.1"
|
||||
@ -3455,6 +3560,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@ -4695,6 +4806,12 @@ dependencies = [
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.30.0"
|
||||
@ -5103,6 +5220,16 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
|
||||
|
||||
[[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.16.20"
|
||||
@ -6778,7 +6905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"idna 0.5.0",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
@ -6852,11 +6979,13 @@ dependencies = [
|
||||
"byteorder",
|
||||
"clap",
|
||||
"hashbrown 0.13.2",
|
||||
"hickory-resolver",
|
||||
"image",
|
||||
"num 0.4.1",
|
||||
"quinn",
|
||||
"rayon",
|
||||
"ron",
|
||||
"rustls",
|
||||
"rustyline",
|
||||
"serde",
|
||||
"specs",
|
||||
@ -8423,6 +8552,16 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.51.0"
|
||||
|
@ -23,7 +23,9 @@ network = { package = "veloren-network", path = "../network", features = ["compr
|
||||
|
||||
byteorder = "1.3.2"
|
||||
tokio = { workspace = true, features = ["rt-multi-thread"] }
|
||||
quinn = "0.10"
|
||||
quinn = { version = "0.10", features = ["rustls"] }
|
||||
rustls = { version = "0.21.6", features = ["dangerous_configuration"] }
|
||||
hickory-resolver = { version = "0.24.0", features = ["system-config", "tokio-runtime"] }
|
||||
image = { workspace = true }
|
||||
num = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
@ -8,12 +8,24 @@ pub enum ConnectionArgs {
|
||||
Quic {
|
||||
hostname: String,
|
||||
prefer_ipv6: bool,
|
||||
validate_tls: bool,
|
||||
},
|
||||
///hostname: (hostname|ip):[<port>]
|
||||
Tcp {
|
||||
hostname: String,
|
||||
prefer_ipv6: bool,
|
||||
},
|
||||
/// SRV lookup
|
||||
///
|
||||
/// SRV lookups can not contain a port, but will be able to connect to
|
||||
/// configured port automatically. If a connection with a port is given,
|
||||
/// this will gracefully fall back to TCP.
|
||||
Srv {
|
||||
hostname: String,
|
||||
prefer_ipv6: bool,
|
||||
validate_tls: bool,
|
||||
use_quic: bool,
|
||||
},
|
||||
Mpsc(u64),
|
||||
}
|
||||
|
||||
@ -51,6 +63,7 @@ pub(crate) async fn resolve(
|
||||
pub(crate) async fn try_connect<F>(
|
||||
network: &network::Network,
|
||||
address: &str,
|
||||
override_port: Option<u16>,
|
||||
prefer_ipv6: bool,
|
||||
f: F,
|
||||
) -> Result<network::Participant, crate::error::Error>
|
||||
@ -59,10 +72,15 @@ where
|
||||
{
|
||||
use crate::error::Error;
|
||||
let mut participant = None;
|
||||
for addr in resolve(address, prefer_ipv6)
|
||||
for mut addr in resolve(address, prefer_ipv6)
|
||||
.await
|
||||
.map_err(Error::HostnameLookupFailed)?
|
||||
{
|
||||
// Override the port if one was passed. Used for SRV lookups which get port info
|
||||
// out-of-band
|
||||
if let Some(port) = override_port {
|
||||
addr.set_port(port);
|
||||
}
|
||||
match network.connect(f(addr)).await {
|
||||
Ok(p) => {
|
||||
participant = Some(Ok(p));
|
||||
|
@ -68,16 +68,22 @@ use common_state::State;
|
||||
use common_systems::add_local_systems;
|
||||
use comp::BuffKind;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use hickory_resolver::{
|
||||
config::{ResolverConfig, ResolverOpts},
|
||||
AsyncResolver,
|
||||
};
|
||||
use image::DynamicImage;
|
||||
use network::{ConnectAddr, Network, Participant, Pid, Stream};
|
||||
use num::traits::FloatConst;
|
||||
use rayon::prelude::*;
|
||||
use rustls::client::ServerCertVerified;
|
||||
use specs::Component;
|
||||
use std::{
|
||||
collections::{BTreeMap, VecDeque},
|
||||
fmt::Debug,
|
||||
mem,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
time::{Duration, Instant, SystemTime},
|
||||
};
|
||||
use tokio::runtime::Runtime;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
@ -327,6 +333,50 @@ pub struct CharacterList {
|
||||
pub loading: bool,
|
||||
}
|
||||
|
||||
async fn connect_quic(
|
||||
network: &Network,
|
||||
hostname: String,
|
||||
override_port: Option<u16>,
|
||||
prefer_ipv6: bool,
|
||||
validate_tls: bool,
|
||||
) -> Result<network::Participant, crate::error::Error> {
|
||||
let config = if validate_tls {
|
||||
quinn::ClientConfig::with_native_roots()
|
||||
} else {
|
||||
warn!(
|
||||
"skipping validation of server identity. There is no guarantee that the server you're \
|
||||
connected to is the one you expect to be connecting to."
|
||||
);
|
||||
struct Verifier;
|
||||
impl rustls::client::ServerCertVerifier for Verifier {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_: &rustls::Certificate,
|
||||
_: &[rustls::Certificate],
|
||||
_: &rustls::ServerName,
|
||||
_: &mut dyn Iterator<Item = &[u8]>,
|
||||
_: &[u8],
|
||||
_: SystemTime,
|
||||
) -> Result<ServerCertVerified, rustls::Error> {
|
||||
Ok(ServerCertVerified::assertion())
|
||||
}
|
||||
}
|
||||
|
||||
let mut cfg = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_custom_certificate_verifier(Arc::new(Verifier))
|
||||
.with_no_client_auth();
|
||||
cfg.enable_early_data = true;
|
||||
|
||||
quinn::ClientConfig::new(Arc::new(cfg))
|
||||
};
|
||||
|
||||
addr::try_connect(network, &hostname, override_port, prefer_ipv6, |a| {
|
||||
ConnectAddr::Quic(a, config.clone(), hostname.clone())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub async fn new(
|
||||
addr: ConnectionArgs,
|
||||
@ -343,24 +393,132 @@ impl Client {
|
||||
let network = Network::new(Pid::new(), &runtime);
|
||||
|
||||
init_stage_update(ClientInitStage::ConnectionEstablish);
|
||||
|
||||
let mut participant = match addr {
|
||||
ConnectionArgs::Srv {
|
||||
hostname,
|
||||
prefer_ipv6,
|
||||
validate_tls,
|
||||
use_quic,
|
||||
} => {
|
||||
// Try to create a resolver backed by /etc/resolv.conf or the Windows Registry
|
||||
// first. If that fails, create a resolver being hard-coded to
|
||||
// Google's 8.8.8.8 public resolver.
|
||||
let resolver = AsyncResolver::tokio_from_system_conf().unwrap_or_else(|error| {
|
||||
error!("Failed to create DNS resolver using system configuration: {error:?}");
|
||||
warn!("Falling back to a default configured resolver.");
|
||||
AsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default())
|
||||
});
|
||||
|
||||
let quic_service_host = format!("_veloren._udp.{hostname}");
|
||||
let quic_lookup_future = resolver.srv_lookup(quic_service_host);
|
||||
let tcp_service_host = format!("_veloren._tcp.{hostname}");
|
||||
let tcp_lookup_future = resolver.srv_lookup(tcp_service_host);
|
||||
let (quic_rr, tcp_rr) = tokio::join!(quic_lookup_future, tcp_lookup_future);
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum ConnMode {
|
||||
Quic,
|
||||
Tcp,
|
||||
}
|
||||
|
||||
// Push the results of both futures into `srv_rr`. This uses map_or_else purely
|
||||
// for side effects.
|
||||
let mut srv_rr = Vec::new();
|
||||
let () = quic_rr.map_or_else(
|
||||
|error| {
|
||||
warn!("QUIC SRV lookup failed: {error:?}");
|
||||
},
|
||||
|srv_lookup| {
|
||||
srv_rr.extend(srv_lookup.iter().cloned().map(|srv| (ConnMode::Quic, srv)))
|
||||
},
|
||||
);
|
||||
let () = tcp_rr.map_or_else(
|
||||
|error| {
|
||||
warn!("TCP SRV lookup failed: {error:?}");
|
||||
},
|
||||
|srv_lookup| {
|
||||
srv_rr.extend(srv_lookup.iter().cloned().map(|srv| (ConnMode::Tcp, srv)))
|
||||
},
|
||||
);
|
||||
|
||||
// SRV records have a priority; lowest priority hosts MUST be contacted first.
|
||||
let srv_rr_slice = srv_rr.as_mut_slice();
|
||||
srv_rr_slice.sort_by_key(|(_, srv)| srv.priority());
|
||||
|
||||
let mut iter = srv_rr_slice.iter();
|
||||
|
||||
// This loops exits as soon as the above iter over `srv_rr_slice` is exhausted
|
||||
loop {
|
||||
if let Some((conn_mode, srv_rr)) = iter.next() {
|
||||
let hostname = format!("{}", srv_rr.target());
|
||||
let port = Some(srv_rr.port());
|
||||
let conn_result = match conn_mode {
|
||||
ConnMode::Quic => {
|
||||
connect_quic(&network, hostname, port, prefer_ipv6, validate_tls)
|
||||
.await
|
||||
},
|
||||
ConnMode::Tcp => {
|
||||
addr::try_connect(
|
||||
&network,
|
||||
&hostname,
|
||||
port,
|
||||
prefer_ipv6,
|
||||
ConnectAddr::Tcp,
|
||||
)
|
||||
.await
|
||||
},
|
||||
};
|
||||
match conn_result {
|
||||
Ok(c) => break c,
|
||||
Err(error) => {
|
||||
warn!("Failed to connect to host {}: {error:?}", srv_rr.target())
|
||||
},
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"No SRV hosts succeeded connection, falling back to direct connection"
|
||||
);
|
||||
// This case is also hit if no SRV host was returned from the query, so we
|
||||
// check for QUIC/TCP preference.
|
||||
let c = if use_quic {
|
||||
connect_quic(&network, hostname, None, prefer_ipv6, validate_tls)
|
||||
.await?
|
||||
} else {
|
||||
match addr::try_connect(
|
||||
&network,
|
||||
&hostname,
|
||||
None,
|
||||
prefer_ipv6,
|
||||
ConnectAddr::Tcp,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
};
|
||||
break c;
|
||||
}
|
||||
}
|
||||
},
|
||||
ConnectionArgs::Tcp {
|
||||
hostname,
|
||||
prefer_ipv6,
|
||||
} => addr::try_connect(&network, &hostname, prefer_ipv6, ConnectAddr::Tcp).await?,
|
||||
} => {
|
||||
addr::try_connect(&network, &hostname, None, prefer_ipv6, ConnectAddr::Tcp).await?
|
||||
},
|
||||
ConnectionArgs::Quic {
|
||||
hostname,
|
||||
prefer_ipv6,
|
||||
validate_tls,
|
||||
} => {
|
||||
warn!(
|
||||
"QUIC is enabled. This is experimental and you won't be able to connect to \
|
||||
TCP servers unless deactivated"
|
||||
);
|
||||
let config = quinn::ClientConfig::with_native_roots();
|
||||
addr::try_connect(&network, &hostname, prefer_ipv6, |a| {
|
||||
ConnectAddr::Quic(a, config.clone(), hostname.clone())
|
||||
})
|
||||
.await?
|
||||
|
||||
connect_quic(&network, hostname, None, prefer_ipv6, validate_tls).await?
|
||||
},
|
||||
ConnectionArgs::Mpsc(id) => network.connect(ConnectAddr::Mpsc(id)).await?,
|
||||
};
|
||||
|
@ -334,7 +334,9 @@ impl PlayState for MainMenuState {
|
||||
server_address,
|
||||
} => {
|
||||
let net_settings = &mut global_state.settings.networking;
|
||||
let use_srv = net_settings.use_srv;
|
||||
let use_quic = net_settings.use_quic;
|
||||
let validate_tls = net_settings.validate_tls;
|
||||
net_settings.username = username.clone();
|
||||
net_settings.default_server = server_address.clone();
|
||||
if !net_settings.servers.contains(&server_address) {
|
||||
@ -344,10 +346,18 @@ impl PlayState for MainMenuState {
|
||||
.settings
|
||||
.save_to_file_warn(&global_state.config_dir);
|
||||
|
||||
let connection_args = if use_quic {
|
||||
let connection_args = if use_srv {
|
||||
ConnectionArgs::Srv {
|
||||
hostname: server_address,
|
||||
prefer_ipv6: false,
|
||||
validate_tls,
|
||||
use_quic,
|
||||
}
|
||||
} else if use_quic {
|
||||
ConnectionArgs::Quic {
|
||||
hostname: server_address,
|
||||
prefer_ipv6: false,
|
||||
validate_tls,
|
||||
}
|
||||
} else {
|
||||
ConnectionArgs::Tcp {
|
||||
|
@ -9,7 +9,9 @@ pub struct NetworkingSettings {
|
||||
pub servers: Vec<String>,
|
||||
pub default_server: String,
|
||||
pub trusted_auth_servers: HashSet<String>,
|
||||
pub use_srv: bool,
|
||||
pub use_quic: bool,
|
||||
pub validate_tls: bool,
|
||||
pub player_physics_behavior: bool,
|
||||
pub lossy_terrain_compression: bool,
|
||||
pub enable_discord_integration: bool,
|
||||
@ -25,7 +27,9 @@ impl Default for NetworkingSettings {
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect(),
|
||||
use_srv: true,
|
||||
use_quic: false,
|
||||
validate_tls: true,
|
||||
player_physics_behavior: false,
|
||||
lossy_terrain_compression: false,
|
||||
enable_discord_integration: true,
|
||||
|
Loading…
Reference in New Issue
Block a user