diff options
author | Gilles Henaux <gill.henaux@gmail.com> | 2023-02-20 21:55:35 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-20 21:55:35 +0100 |
commit | b3b926fb27012bf5f975efd3cd80dab0c65bda2e (patch) | |
tree | 38a9eeee0ad07b8ee6e970b524133b116e7bb767 | |
parent | 81527b24060ab73f5bdaad252a2d62fd42f36dd7 (diff) | |
parent | c77e90c7b322b8ada71590d938150579b76431d5 (diff) |
Merge pull request #14 from Henauxg/ipv6-handling
[client & server] Fix IPv6 handling
-rw-r--r-- | CHANGELOG.md | 11 | ||||
-rw-r--r-- | README.md | 13 | ||||
-rw-r--r-- | examples/breakout/breakout.rs | 3 | ||||
-rw-r--r-- | examples/breakout/client.rs | 10 | ||||
-rw-r--r-- | examples/breakout/server.rs | 20 | ||||
-rw-r--r-- | examples/chat/client.rs | 2 | ||||
-rw-r--r-- | examples/chat/server.rs | 8 | ||||
-rw-r--r-- | src/client/connection.rs | 164 | ||||
-rw-r--r-- | src/server.rs | 101 | ||||
-rw-r--r-- | src/server/certificate.rs | 23 | ||||
-rw-r--r-- | tests/certificates.rs | 14 | ||||
-rw-r--r-- | tests/utils/mod.rs | 19 |
12 files changed, 266 insertions, 122 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f8543bd..4b82593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## Version 0.4.0-dev + +- [client]: Fix IPv6 handling. + - Remove `ConnectionConfiguration::new` + - Add `ConnectionConfiguration::from_strings`, `ConnectionConfiguration::from_ips` and `ConnectionConfiguration::from_addrs` +- [server]: Fix IPv6 handling. + - Rename `ServerConfigurationData` to `ServerConfiguration`. + - Remove `ServerConfiguration::new` + - Add `ServerConfiguration::from_string`, `ServerConfiguration::from_ip` and `ServerConfiguration::from_addr` + - Add `server_hostname` to `CertificateRetrievalMode::GenerateSelfSigned` and `CertificateRetrievalMode::LoadFromFileOrGenerateSelfSigned` + ## Version 0.3.0 (2023-01-20) ### Added @@ -95,10 +95,10 @@ This is a bird-eye view of the features/tasks that will probably be worked on ne fn start_connection(client: ResMut<Client>) { client .open_connection( - ClientConfigurationData::new( - "127.0.0.1".to_string(), + ClientConfigurationData::from_ips( + IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6000, - "0.0.0.0".to_string(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0, ), CertificateVerificationMode::SkipVerification, @@ -143,7 +143,7 @@ fn handle_server_messages( fn start_listening(mut server: ResMut<Server>) { server .start_endpoint( - ServerConfigurationData::new("127.0.0.1".to_string(), 6000, "0.0.0.0".to_string()), + ServerConfigurationData::from_ip(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 6000), CertificateRetrievalMode::GenerateSelfSigned, ) .unwrap(); @@ -258,7 +258,9 @@ On the server: ```rust // To generate a new self-signed certificate on each startup - server.start_endpoint(/*...*/, CertificateRetrievalMode::GenerateSelfSigned); + server.start_endpoint(/*...*/, CertificateRetrievalMode::GenerateSelfSigned { + server_hostname: "127.0.0.1".to_string(), + }); // To load a pre-existing one from files server.start_endpoint(/*...*/, CertificateRetrievalMode::LoadFromFile { cert_file: "./certificates.pem".into(), @@ -269,6 +271,7 @@ On the server: cert_file: "./certificates.pem".into(), key_file: "./privkey.pem".into(), save_on_disk: true, // To persist on disk if generated + server_hostname: "127.0.0.1".to_string(), }); ``` diff --git a/examples/breakout/breakout.rs b/examples/breakout/breakout.rs index e57e2d7..31fcf01 100644 --- a/examples/breakout/breakout.rs +++ b/examples/breakout/breakout.rs @@ -1,6 +1,8 @@ //! A simplified implementation of the classic game "Breakout". //! => Original example by Bevy, modified for Bevy Quinnet to add a 2 players versus mode. +use std::net::{IpAddr, Ipv4Addr}; + use bevy::{ecs::schedule::ShouldRun, prelude::*, time::FixedTimestep}; use bevy_quinnet::{ client::QuinnetClientPlugin, @@ -13,6 +15,7 @@ mod protocol; mod server; const SERVER_HOST: &str = "127.0.0.1"; +const LOCAL_BIND_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); const SERVER_PORT: u16 = 6000; // Defines the amount of time that should elapse between each physics step. diff --git a/examples/breakout/client.rs b/examples/breakout/client.rs index 33873d3..be4435a 100644 --- a/examples/breakout/client.rs +++ b/examples/breakout/client.rs @@ -24,7 +24,8 @@ use bevy_quinnet::{ use crate::{ protocol::{ClientMessage, PaddleInput, ServerMessage}, BrickId, CollisionEvent, CollisionSound, GameState, Score, Velocity, WallLocation, BALL_SIZE, - BALL_SPEED, BRICK_SIZE, GAP_BETWEEN_BRICKS, PADDLE_SIZE, SERVER_HOST, SERVER_PORT, TIME_STEP, + BALL_SPEED, BRICK_SIZE, GAP_BETWEEN_BRICKS, LOCAL_BIND_IP, PADDLE_SIZE, SERVER_HOST, + SERVER_PORT, TIME_STEP, }; const SCOREBOARD_FONT_SIZE: f32 = 40.0; @@ -91,14 +92,13 @@ struct WallBundle { #[bundle] sprite_bundle: SpriteBundle, } - pub(crate) fn start_connection(mut client: ResMut<Client>) { client .open_connection( - ConnectionConfiguration::new( - SERVER_HOST.to_string(), + ConnectionConfiguration::from_ips( + SERVER_HOST.parse().unwrap(), SERVER_PORT, - "0.0.0.0".to_string(), + LOCAL_BIND_IP, 0, ), CertificateVerificationMode::SkipVerification, diff --git a/examples/breakout/server.rs b/examples/breakout/server.rs index 4164f7e..f2389bd 100644 --- a/examples/breakout/server.rs +++ b/examples/breakout/server.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap}; use bevy::{ prelude::{ @@ -9,9 +9,7 @@ use bevy::{ transform::TransformBundle, }; use bevy_quinnet::{ - server::{ - certificate::CertificateRetrievalMode, ConnectionEvent, Server, ServerConfigurationData, - }, + server::{certificate::CertificateRetrievalMode, ConnectionEvent, Server, ServerConfiguration}, shared::{channel::ChannelId, ClientId}, }; @@ -19,8 +17,8 @@ use crate::{ protocol::{ClientMessage, PaddleInput, ServerMessage}, BrickId, Velocity, WallLocation, BALL_SIZE, BALL_SPEED, BOTTOM_WALL, BRICK_SIZE, GAP_BETWEEN_BRICKS, GAP_BETWEEN_BRICKS_AND_SIDES, GAP_BETWEEN_PADDLE_AND_BRICKS, - GAP_BETWEEN_PADDLE_AND_FLOOR, LEFT_WALL, PADDLE_PADDING, PADDLE_SIZE, PADDLE_SPEED, RIGHT_WALL, - SERVER_HOST, SERVER_PORT, TIME_STEP, TOP_WALL, WALL_THICKNESS, + GAP_BETWEEN_PADDLE_AND_FLOOR, LEFT_WALL, LOCAL_BIND_IP, PADDLE_PADDING, PADDLE_SIZE, + PADDLE_SPEED, RIGHT_WALL, SERVER_HOST, SERVER_PORT, TIME_STEP, TOP_WALL, WALL_THICKNESS, }; const GAP_BETWEEN_PADDLE_AND_BALL: f32 = 35.; @@ -81,12 +79,10 @@ struct WallBundle { pub(crate) fn start_listening(mut server: ResMut<Server>) { server .start_endpoint( - ServerConfigurationData::new( - SERVER_HOST.to_string(), - SERVER_PORT, - "0.0.0.0".to_string(), - ), - CertificateRetrievalMode::GenerateSelfSigned, + ServerConfiguration::from_ip(LOCAL_BIND_IP, SERVER_PORT), + CertificateRetrievalMode::GenerateSelfSigned { + server_hostname: SERVER_HOST.to_string(), + }, ) .unwrap(); } diff --git a/examples/chat/client.rs b/examples/chat/client.rs index 6cb25e4..fae4976 100644 --- a/examples/chat/client.rs +++ b/examples/chat/client.rs @@ -120,7 +120,7 @@ fn start_terminal_listener(mut commands: Commands) { fn start_connection(mut client: ResMut<Client>) { client .open_connection( - ConnectionConfiguration::new("127.0.0.1".to_string(), 6000, "0.0.0.0".to_string(), 0), + ConnectionConfiguration::from_strings("127.0.0.1:6000", "0.0.0.0:0").unwrap(), CertificateVerificationMode::SkipVerification, ) .unwrap(); diff --git a/examples/chat/server.rs b/examples/chat/server.rs index 8c89f2b..da28f88 100644 --- a/examples/chat/server.rs +++ b/examples/chat/server.rs @@ -4,7 +4,7 @@ use bevy::{app::ScheduleRunnerPlugin, log::LogPlugin, prelude::*}; use bevy_quinnet::{ server::{ certificate::CertificateRetrievalMode, ConnectionLostEvent, Endpoint, QuinnetServerPlugin, - Server, ServerConfigurationData, + Server, ServerConfiguration, }, shared::{channel::ChannelId, ClientId}, }; @@ -116,8 +116,10 @@ fn handle_disconnect(endpoint: &mut Endpoint, users: &mut ResMut<Users>, client_ fn start_listening(mut server: ResMut<Server>) { server .start_endpoint( - ServerConfigurationData::new("127.0.0.1".to_string(), 6000, "0.0.0.0".to_string()), - CertificateRetrievalMode::GenerateSelfSigned, + ServerConfiguration::from_string("0.0.0.0:6000").unwrap(), + CertificateRetrievalMode::GenerateSelfSigned { + server_hostname: "127.0.0.1".to_string(), + }, ) .unwrap(); } diff --git a/src/client/connection.rs b/src/client/connection.rs index f7538f6..05ab185 100644 --- a/src/client/connection.rs +++ b/src/client/connection.rs @@ -1,4 +1,9 @@ -use std::{collections::HashMap, error::Error, net::SocketAddr, sync::Arc}; +use std::{ + collections::HashMap, + error::Error, + net::{AddrParseError, IpAddr, SocketAddr}, + sync::Arc, +}; use bevy::prelude::{error, info}; use bytes::Bytes; @@ -45,10 +50,9 @@ pub struct ConnectionLostEvent { /// Configuration of a client connection, used when connecting to a server #[derive(Debug, Deserialize, Clone)] pub struct ConnectionConfiguration { - server_host: String, - server_port: u16, - local_bind_host: String, - local_bind_port: u16, + server_addr: SocketAddr, + server_hostname: String, + local_bind_addr: SocketAddr, } impl ConnectionConfiguration { @@ -56,33 +60,133 @@ impl ConnectionConfiguration { /// /// # Arguments /// - /// * `server_host` - Address of the server - /// * `server_port` - Port that the server is listening on - /// * `local_bind_host` - Local address to bind to, which should usually be a wildcard address like `0.0.0.0` or `[::]`, which allow communication with any reachable IPv4 or IPv6 address. See [`quinn::endpoint::Endpoint`] for more precision - /// * `local_bind_port` - Local port to bind to. Use 0 to get an OS-assigned port.. See [`quinn::endpoint::Endpoint`] for more precision + /// * `server_addr_str` - IP address and port of the server + /// * `local_bind_addr_str` - Local address and port to bind to separated by `:`. The address should usually be a wildcard like `0.0.0.0` (for an IPv4) or `[::]` (for an IPv6), which allow communication with any reachable IPv4 or IPv6 address. See [`std::net::SocketAddrV4`] and [`std::net::SocketAddrV6`] or [`quinn::endpoint::Endpoint`] for more precision. For the local port to bind to, use 0 to get an OS-assigned port. /// /// # Examples /// + /// Connect to an IPv4 server hosted on localhost (127.0.0.1), which is listening on port 6000. Use 0 as a local bind port to let the OS assign a port. + /// ``` + /// use bevy_quinnet::client::connection::ConnectionConfiguration; + /// let config = ConnectionConfiguration::from_strings( + /// "127.0.0.1:6000", + /// "0.0.0.0:0" + /// ); + /// ``` + /// Connect to an IPv6 server hosted on localhost (::1), which is listening on port 6000. Use 0 as a local bind port to let the OS assign a port. /// ``` /// use bevy_quinnet::client::connection::ConnectionConfiguration; - /// let config = ConnectionConfiguration::new( - /// "127.0.0.1".to_string(), - /// 6000, - /// "0.0.0.0".to_string(), - /// 0, - /// ); + /// let config = ConnectionConfiguration::from_strings( + /// "[::1]:6000", + /// "[::]:0" + /// ); /// ``` - pub fn new( - server_host: String, + pub fn from_strings( + server_addr_str: &str, + local_bind_addr_str: &str, + ) -> Result<Self, AddrParseError> { + let server_addr = server_addr_str.parse()?; + let local_bind_addr = local_bind_addr_str.parse()?; + Ok(Self::from_addrs(server_addr, local_bind_addr)) + } + + /// Same as [`ConnectionConfiguration::from_strings`], but with an additional `server_hostname` for certificate verification if it is not just the server IP. + pub fn from_strings_with_name( + server_addr_str: &str, + server_hostname: String, + local_bind_addr_str: &str, + ) -> Result<Self, AddrParseError> { + Ok(Self::from_addrs_with_name( + server_addr_str.parse()?, + server_hostname, + local_bind_addr_str.parse()?, + )) + } + + /// Creates a new ConnectionConfiguration + /// + /// # Arguments + /// + /// * `server_ip` - IP address of the server + /// * `server_port` - Port of the server + /// * `local_bind_ip` - Local IP address to bind to. The address should usually be a wildcard like `0.0.0.0` (for an IPv4) or `0:0:0:0:0:0:0:0` (for an IPv6), which allow communication with any reachable IPv4 or IPv6 address. See [`std::net::Ipv4Addr`] and [`std::net::Ipv6Addr`] for more precision. + /// * `local_bind_port` - Local port to bind to. Use 0 to get an OS-assigned port. + /// + /// # Examples + /// + /// Connect to an IPv4 server hosted on localhost (127.0.0.1), which is listening on port 6000. Use 0 as a local bind port to let the OS assign a port. + /// ``` + /// use bevy_quinnet::client::connection::ConnectionConfiguration; + /// let config = ConnectionConfiguration::from_ips( + /// IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), + /// 6000, + /// IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + /// 0 + /// ); + /// ``` + pub fn from_ips( + server_ip: IpAddr, + server_port: u16, + local_bind_ip: IpAddr, + local_bind_port: u16, + ) -> Self { + Self::from_addrs( + SocketAddr::new(server_ip, server_port), + SocketAddr::new(local_bind_ip, local_bind_port), + ) + } + + /// Same as [`ConnectionConfiguration::from_ips`], but with an additional `server_hostname` for certificate verification if it is not just the server IP. + pub fn from_ips_with_name( + server_ip: IpAddr, server_port: u16, - local_bind_host: String, + server_hostname: String, + local_bind_ip: IpAddr, local_bind_port: u16, ) -> Self { + Self::from_addrs_with_name( + SocketAddr::new(server_ip, server_port), + server_hostname, + SocketAddr::new(local_bind_ip, local_bind_port), + ) + } + + /// Creates a new ConnectionConfiguration + /// + /// # Arguments + /// + /// * `server_addr` - IP address and port of the server + /// * `local_bind_addr` - Local address and port to bind to. For the local port to bind to, use 0 to get an OS-assigned port. + /// + /// # Examples + /// + /// Connect to an IPv4 server hosted on localhost (127.0.0.1), which is listening on port 6000. Use 0 as a local bind port to let the OS assign a port. + /// ``` + /// use bevy_quinnet::client::connection::ConnectionConfiguration; + /// use std::{net::{IpAddr, Ipv4Addr, SocketAddr}}; + /// let config = ConnectionConfiguration::from_addrs( + /// SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6000), + /// SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), + /// ); + /// ``` + pub fn from_addrs(server_addr: SocketAddr, local_bind_addr: SocketAddr) -> Self { + Self { + server_addr, + server_hostname: server_addr.ip().to_string(), + local_bind_addr, + } + } + + /// Same as [`ConnectionConfiguration::from_addrs`], but with an additional `server_hostname` for certificate verification if it is not just the server IP. + pub fn from_addrs_with_name( + server_addr: SocketAddr, + server_hostname: String, + local_bind_addr: SocketAddr, + ) -> Self { Self { - server_host, - server_port, - local_bind_host, - local_bind_port, + server_addr, + server_hostname, + local_bind_addr, } } } @@ -380,28 +484,20 @@ pub(crate) async fn connection_task( close_recv: broadcast::Receiver<()>, bytes_from_server_send: mpsc::Sender<Bytes>, ) { - let server_adr_str = format!("{}:{}", config.server_host, config.server_port); - let srv_host = config.server_host.clone(); - let local_bind_adr = format!("{}:{}", config.local_bind_host, config.local_bind_port); - info!( "Connection {} trying to connect to server on: {} ...", - connection_id, server_adr_str + connection_id, config.server_addr ); - let server_addr: SocketAddr = server_adr_str - .parse() - .expect("Failed to parse server address"); - let client_cfg = configure_client(cert_mode, to_sync_client_send.clone()) .expect("Failed to configure client"); - let mut endpoint = Endpoint::client(local_bind_adr.parse().unwrap()) - .expect("Failed to create client endpoint"); + let mut endpoint = + Endpoint::client(config.local_bind_addr).expect("Failed to create client endpoint"); endpoint.set_default_client_config(client_cfg); let connection = endpoint - .connect(server_addr, &srv_host) // TODO Clean: error handling + .connect(config.server_addr, &config.server_hostname) .expect("Failed to connect: configuration error") .await; match connection { diff --git a/src/server.rs b/src/server.rs index c2ab409..ae3bd82 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,6 @@ use std::{ collections::{HashMap, HashSet}, - net::SocketAddr, + net::{AddrParseError, IpAddr, SocketAddr}, sync::Arc, time::Duration, }; @@ -52,50 +52,76 @@ pub struct ConnectionLostEvent { pub id: ClientId, } -/// Configuration of the server, used when the server starts -/// -/// # Examples -/// -/// ``` -/// use bevy_quinnet::server::ServerConfigurationData; -/// let config = ServerConfigurationData::new( -/// "127.0.0.1".to_string(), -/// 6000, -/// "0.0.0.0".to_string()); -/// ``` +/// Configuration of the server, used when the server starts an Endpoint #[derive(Debug, Deserialize, Clone)] -pub struct ServerConfigurationData { - host: String, - port: u16, - local_bind_host: String, +pub struct ServerConfiguration { + local_bind_addr: SocketAddr, } -impl ServerConfigurationData { - /// Creates a new ServerConfigurationData +impl ServerConfiguration { + /// Creates a new ServerConfiguration /// /// # Arguments /// - /// * `host` - Address of the server - /// * `port` - Port that the server is listening on - /// * `local_bind_host` - Local address to bind to, which should usually be a wildcard address like `0.0.0.0` or `[::]`, which allow communication with any reachable IPv4 or IPv6 address. See [`quinn::endpoint::Endpoint`] for more precision + /// * `local_bind_addr_str` - Local address and port to bind to separated by `:`. The address should usually be a wildcard like `0.0.0.0` (for an IPv4) or `[::]` (for an IPv6), which allow communication with any reachable IPv4 or IPv6 address. See [`std::net::SocketAddrV4`] and [`std::net::SocketAddrV6`] or [`quinn::endpoint::Endpoint`] for more precision. /// /// # Examples /// + /// Listen on port 6000, on an IPv4 endpoint, for all incoming IPs. /// ``` - /// use bevy_quinnet::server::ServerConfigurationData; - /// let config = ServerConfigurationData::new( - /// "127.0.0.1".to_string(), - /// 6000, - /// "0.0.0.0".to_string(), - /// ); + /// use bevy_quinnet::server::ServerConfiguration; + /// let config = ServerConfiguration::from_string("0.0.0.0:6000"); /// ``` - pub fn new(host: String, port: u16, local_bind_host: String) -> Self { + /// Listen on port 6000, on an IPv6 endpoint, for all incoming IPs. + /// ``` + /// use bevy_quinnet::server::ServerConfiguration; + /// let config = ServerConfiguration::from_string("[::]:6000"); + /// ``` + pub fn from_string(local_bind_addr_str: &str) -> Result<Self, AddrParseError> { + let local_bind_addr = local_bind_addr_str.parse()?; + Ok(Self { local_bind_addr }) + } + + /// Creates a new ServerConfiguration + /// + /// # Arguments + /// + /// * `local_bind_ip` - Local IP address to bind to. The address should usually be a wildcard like `0.0.0.0` (for an IPv4) or `0:0:0:0:0:0:0:0` (for an IPv6), which allow communication with any reachable IPv4 or IPv6 address. See [`std::net::Ipv4Addr`] and [`std::net::Ipv6Addr`] for more precision. + /// * `local_bind_port` - Local port to bind to. + /// + /// # Examples + /// + /// Listen on port 6000, on an IPv4 endpoint, for all incoming IPs. + /// ``` + /// use bevy_quinnet::server::ServerConfiguration; + /// let config = ServerConfiguration::from_ip(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 6000); + /// ``` + pub fn from_ip(local_bind_ip: IpAddr, local_bind_port: u16) -> Self { Self { - host, - port, - local_bind_host, + local_bind_addr: SocketAddr::new(local_bind_ip, local_bind_port), } } + + /// Creates a new ServerConfiguration + /// + /// # Arguments + /// + /// * `local_bind_addr` - Local address and port to bind to. + /// See [`std::net::SocketAddrV4`] and [`std::net::SocketAddrV6`] for more precision. + /// + /// # Examples + /// + /// Listen on port 6000, on an IPv4 endpoint, for all incoming IPs. + /// ``` + /// use bevy_quinnet::server::ServerConfiguration; + /// use std::{net::{IpAddr, Ipv4Addr, SocketAddr}}; + /// let config = ServerConfiguration::from_addr( + /// SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 6000), + /// ); + /// ``` + pub fn from_addr(local_bind_addr: SocketAddr) -> Self { + Self { local_bind_addr } + } } #[derive(Debug)] @@ -635,19 +661,16 @@ impl Server { self.endpoint.as_mut() } - /// Starts a new endpoint with the given [ServerConfigurationData] and [CertificateRetrievalMode] and opens the default channels. + /// Starts a new endpoint with the given [ServerConfiguration] and [CertificateRetrievalMode] and opens the default channels. /// /// Returns a tuple of the [ServerCertificate] generated or loaded, and the default [ChannelId] pub fn start_endpoint( &mut self, - config: ServerConfigurationData, + config: ServerConfiguration, cert_mode: CertificateRetrievalMode, ) -> Result<(ServerCertificate, ChannelId), QuinnetError> { - let server_adr_str = format!("{}:{}", config.local_bind_host, config.port); - let server_addr = server_adr_str.parse::<SocketAddr>()?; - // Endpoint configuration - let server_cert = retrieve_certificate(&config.host, cert_mode)?; + let server_cert = retrieve_certificate(cert_mode)?; let mut server_config = ServerConfig::with_single_cert( server_cert.cert_chain.clone(), server_cert.priv_key.clone(), @@ -661,12 +684,12 @@ impl Server { let (endpoint_close_send, endpoint_close_recv) = broadcast::channel(DEFAULT_KILL_MESSAGE_QUEUE_SIZE); - info!("Starting endpoint on: {} ...", server_adr_str); + info!("Starting endpoint on: {} ...", config.local_bind_addr); self.runtime.spawn(async move { endpoint_task( server_config, - server_addr, + config.local_bind_addr, to_sync_server_send.clone(), endpoint_close_recv, ) diff --git a/src/server/certificate.rs b/src/server/certificate.rs index f13ce16..00bbad3 100644 --- a/src/server/certificate.rs +++ b/src/server/certificate.rs @@ -11,8 +11,8 @@ use crate::shared::{CertificateFingerprint, QuinnetError}; /// Represents the origin of a certificate. #[derive(Debug, Clone)] pub enum CertOrigin { - /// Indicates that the certificate was generated. The `server_host` field contains the hostname used when generating the certificate. - Generated { server_host: String }, + /// Indicates that the certificate was generated. The `server_hostname` field contains the hostname used when generating the certificate. + Generated { server_hostname: String }, /// Indicates that the certificate was loaded from a file. Loaded, } @@ -20,16 +20,17 @@ pub enum CertOrigin { /// How the server should retrieve its certificate. #[derive(Debug, Clone)] pub enum CertificateRetrievalMode { - /// The server will always generate a new self-signed certificate when starting up - GenerateSelfSigned, - /// Try to load cert & key from files. + /// The server will always generate a new self-signed certificate when starting up, using `server_hostname` as the subject of the certificate. + GenerateSelfSigned { server_hostname: String }, + /// Try to load cert & key from files `cert_file``and `key_file`. LoadFromFile { cert_file: String, key_file: String }, - /// Try to load cert & key from files. - /// If the files do not exist, generate a self-signed certificate, and optionally save it to disk. + /// Try to load cert & key from files `cert_file``and `key_file`. + /// If the files do not exist, generate a self-signed certificate using `server_hostname` as the subject of the certificate. Optionally save it to disk if `save_on_disk` is enabled. LoadFromFileOrGenerateSelfSigned { cert_file: String, key_file: String, save_on_disk: bool, + server_hostname: String, }, } @@ -111,12 +112,11 @@ fn generate_self_signed_certificate( } pub(crate) fn retrieve_certificate( - server_host: &String, cert_mode: CertificateRetrievalMode, ) -> Result<ServerCertificate, QuinnetError> { match cert_mode { - CertificateRetrievalMode::GenerateSelfSigned => { - let (server_cert, _rcgen_cert) = generate_self_signed_certificate(server_host)?; + CertificateRetrievalMode::GenerateSelfSigned { server_hostname } => { + let (server_cert, _rcgen_cert) = generate_self_signed_certificate(&server_hostname)?; trace!("Generatied a new self-signed certificate"); Ok(server_cert) } @@ -132,6 +132,7 @@ pub(crate) fn retrieve_certificate( save_on_disk, cert_file, key_file, + server_hostname, } => { if Path::new(&cert_file).exists() && Path::new(&key_file).exists() { let server_cert = read_certs_from_files(&cert_file, &key_file)?; @@ -139,7 +140,7 @@ pub(crate) fn retrieve_certificate( Ok(server_cert) } else { warn!("{} and/or {} do not exist, could not load existing certificate. Generating a new self-signed certificate.", cert_file, key_file); - let (server_cert, rcgen_cert) = generate_self_signed_certificate(server_host)?; + let (server_cert, rcgen_cert) = generate_self_signed_certificate(&server_hostname)?; if save_on_disk { write_certs_to_files(&rcgen_cert, &cert_file, &key_file)?; trace!("Successfuly saved cert and key to files"); diff --git a/tests/certificates.rs b/tests/certificates.rs index 21daec7..1745b0d 100644 --- a/tests/certificates.rs +++ b/tests/certificates.rs @@ -8,7 +8,7 @@ use bevy_quinnet::{ Client, QuinnetClientPlugin, DEFAULT_KNOWN_HOSTS_FILE, }, server::{ - certificate::CertificateRetrievalMode, QuinnetServerPlugin, Server, ServerConfigurationData, + certificate::CertificateRetrievalMode, QuinnetServerPlugin, Server, ServerConfiguration, }, }; @@ -72,7 +72,7 @@ fn trust_on_first_use() { let mut server = server_app.world.resource_mut::<Server>(); let (server_cert, _) = server .start_endpoint( - ServerConfigurationData::new(SERVER_HOST.to_string(), port, "0.0.0.0".to_string()), + ServerConfiguration::from_ip("0.0.0.0".parse().unwrap(), port), CertificateRetrievalMode::LoadFromFile { cert_file: TEST_CERT_FILE.to_string(), key_file: TEST_KEY_FILE.to_string(), @@ -130,7 +130,7 @@ fn trust_on_first_use() { ); assert_eq!( cert_info.server_name.to_string(), - SERVER_HOST.to_string(), + SERVER_IP.to_string(), "The server name should match the one we configured" ); @@ -200,8 +200,10 @@ fn trust_on_first_use() { .world .resource_mut::<Server>() .start_endpoint( - ServerConfigurationData::new(SERVER_HOST.to_string(), port, "0.0.0.0".to_string()), - CertificateRetrievalMode::GenerateSelfSigned, + ServerConfiguration::from_ip(LOCAL_BIND_IP, port), + CertificateRetrievalMode::GenerateSelfSigned { + server_hostname: SERVER_IP.to_string(), + }, ) .unwrap(); @@ -270,7 +272,7 @@ fn trust_on_first_use() { ); assert_eq!( interaction_cert_info.server_name.to_string(), - SERVER_HOST.to_string(), + SERVER_IP.to_string(), "The server name in the certificate interaction event should be the server we want to connect to" ); assert_eq!( diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index 24b0ac8..55a1353 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -1,4 +1,8 @@ -use std::{thread::sleep, time::Duration}; +use std::{ + net::{IpAddr, Ipv4Addr}, + thread::sleep, + time::Duration, +}; use bevy::{ app::ScheduleRunnerPlugin, @@ -17,7 +21,7 @@ use bevy_quinnet::{ }, server::{ self, certificate::CertificateRetrievalMode, QuinnetServerPlugin, Server, - ServerConfigurationData, + ServerConfiguration, }, shared::{ channel::{ChannelId, ChannelType}, @@ -56,7 +60,8 @@ pub enum SharedMessage { TestMessage(String), } -pub const SERVER_HOST: &str = "127.0.0.1"; +pub const SERVER_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); +pub const LOCAL_BIND_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); pub fn build_client_app() -> App { let mut client_app = App::new(); @@ -81,7 +86,7 @@ pub fn build_server_app() -> App { } pub fn default_client_configuration(port: u16) -> ConnectionConfiguration { - ConnectionConfiguration::new(SERVER_HOST.to_string(), port, "0.0.0.0".to_string(), 0) + ConnectionConfiguration::from_ips(SERVER_IP, port, LOCAL_BIND_IP, 0) } pub fn start_simple_connection(mut client: ResMut<Client>, port: Res<Port>) { @@ -96,8 +101,10 @@ pub fn start_simple_connection(mut client: ResMut<Client>, port: Res<Port>) { pub fn start_listening(mut server: ResMut<Server>, port: Res<Port>) { server .start_endpoint( - ServerConfigurationData::new(SERVER_HOST.to_string(), port.0, "0.0.0.0".to_string()), - CertificateRetrievalMode::GenerateSelfSigned, + ServerConfiguration::from_ip(LOCAL_BIND_IP, port.0), + CertificateRetrievalMode::GenerateSelfSigned { + server_hostname: SERVER_IP.to_string(), + }, ) .unwrap(); } |