1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use serde::{Deserialize, Serialize};
use std::{
net::{IpAddr, Ipv4Addr},
path::PathBuf,
};
//-------------------------------------------------------------------------------------------------------------------
/// Configuration details for setting up a renet2 server.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GameServerSetupConfig {
/// Protocol id for server/client compatibility.
pub protocol_id: u64,
/// How long connect tokens should live before expiring.
pub expire_secs: u64,
/// Internal connection timeout for clients and servers.
pub timeout_secs: i32,
/// The server's IP address. Used for both native and WASM server sockets.
///
/// This will be the *local* IP. To connect to the internet you likely need to set [`Self::proxy_ip`].
pub server_ip: IpAddr,
/// Port for native sockets.
///
/// Set it to `0` if you don't need to target a specific port.
pub native_port: u16,
/// Port for webtransport sockets.
///
/// Set it to `0` if you don't need to target a specific port.
pub wasm_wt_port: u16,
/// Port for websockets.
///
/// Set it to `0` if you don't need to target a specific port.
pub wasm_ws_port: u16,
/// Public-facing port for native sockets.
///
/// Set it to `0` to fall back to [`Self::native_port`].
pub native_port_proxy: u16,
/// Public-facing port for webtransport sockets.
///
/// Set it to `0` to fall back to [`Self::wasm_wt_port`].
pub wasm_wt_port_proxy: u16,
/// Public-facing port for websockets.
///
/// Set it to `0` to fall back to [`Self::wasm_ws_port`].
pub wasm_ws_port_proxy: u16,
/// Proxy IP address to send to clients in connect tokens instead of the `server_ip`.
///
/// Proxy IP addresses will be associated with the local ports assigned to each socket.
pub proxy_ip: Option<IpAddr>,
/// Domain name to use instead of the proxy_ip for websocket servers.
///
/// This is required if using [`Self::wss_certs`].
pub ws_domain: Option<String>,
/// Location of certificate files to use for websocket servers.
///
/// Format: (cert chain, private key).
/// Files must be PEM encoded.
pub wss_certs: Option<(PathBuf, PathBuf)>,
/// Indicates if there is a TLS proxy set up for websocket connections.
///
/// If this is true then [`Self::wss_certs`] should be `None`.
pub has_wss_proxy: bool,
}
impl GameServerSetupConfig {
/// Make a dummy config.
///
/// Should not be used to connect to a real renet server.
pub fn dummy() -> Self {
Self {
protocol_id: 0u64,
expire_secs: 10u64,
timeout_secs: 5i32,
server_ip: Ipv4Addr::LOCALHOST.into(),
native_port: 0,
wasm_wt_port: 0,
wasm_ws_port: 0,
native_port_proxy: 0,
wasm_wt_port_proxy: 0,
wasm_ws_port_proxy: 0,
proxy_ip: None,
wss_certs: None,
ws_domain: None,
has_wss_proxy: false,
}
}
#[cfg(feature = "ws_server_transport")]
#[allow(clippy::needless_return, reason = "needed when certain features are enabled")]
pub fn get_ws_acceptor(&self) -> Result<renet2_netcode::WebSocketAcceptor, String> {
let Some((cert_chain, privkey)) = &self.wss_certs else {
return Ok(renet2_netcode::WebSocketAcceptor::Plain {
has_tls_proxy: self.has_wss_proxy,
});
};
#[cfg(feature = "ws-native-tls")]
{
let config = Self::get_native_tls_acceptor(cert_chain, privkey)?;
return Ok(renet2_netcode::WebSocketAcceptor::NativeTls(config.into()));
}
#[cfg(all(feature = "ws-rustls", not(feature = "ws-native-tls")))]
{
let config = Self::get_rustls_server_config(cert_chain, privkey)?;
return Ok(renet2_netcode::WebSocketAcceptor::Rustls(config.into()));
}
#[cfg(not(any(feature = "ws-native-tls", feature = "ws-rustls")))]
{
Err(format!(
"failed getting websocket acceptor for certs {cert_chain:?} and {privkey:?}; missing feature ws-native-tls or \
ws-rustls"
))
}
}
/// Format: (cert chain, private key).
/// Files must be PEM encoded. The certs must be x509 and the privkey must be PKCS #8.
#[cfg(feature = "ws-native-tls")]
pub fn get_native_tls_acceptor(cert_chain: &PathBuf, privkey: &PathBuf) -> Result<tokio_native_tls::native_tls::TlsAcceptor, String> {
let certs = std::fs::read(cert_chain)
.map_err(|err| format!("failed reading cert chain at {cert_chain:?} for native tls acceptor: {err:?}"))?;
let privkey =
std::fs::read(privkey).map_err(|err| format!("failed reading privkey at {privkey:?} for native tls acceptor: {err:?}"))?;
let identity = tokio_native_tls::native_tls::Identity::from_pkcs8(&certs, &privkey)
.map_err(|err| format!("failed constructing native tls Identity: {err:?}"))?;
tokio_native_tls::native_tls::TlsAcceptor::new(identity)
.map_err(|err| format!("failed constructing native tls TlsAcceptor: {err:?}"))
}
/// Format: (cert chain, private key).
/// Files must be PEM encoded.
///
/// If there is no `rustls::crypto::CryptoProvider` installed, then the `ring` default provider will be
/// auto-installed.
#[cfg(feature = "ws-rustls")]
pub fn get_rustls_server_config(cert_chain: &PathBuf, privkey: &PathBuf) -> Result<std::sync::Arc<rustls::ServerConfig>, String> {
use rustls_pki_types::pem::PemObject;
let mut file_iter = rustls_pki_types::CertificateDer::pem_file_iter(cert_chain)
.map_err(|err| format!("failed reading {cert_chain:?} for websocket certs: {err:?}"))?;
let mut certs = Vec::default();
file_iter.try_for_each(|i| {
let cert = i.map_err(|err| format!("failure while reading {cert_chain:?} for websocket certs: {err:?}"))?;
certs.push(cert);
Ok::<(), String>(())
})?;
let privkey = rustls_pki_types::PrivateKeyDer::from_pem_file(privkey)
.map_err(|err| format!("failed reading {privkey:?} for websocket certs privkey: {err:?}"))?;
if rustls::crypto::CryptoProvider::get_default().is_none() {
let _ = rustls::crypto::ring::default_provider().install_default();
}
let config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, privkey)
.map_err(|err| format!("failed building rustls serverconfig with websocket certs: {err:?}"))?;
Ok(std::sync::Arc::new(config))
}
}
//-------------------------------------------------------------------------------------------------------------------