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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
use serde::Deserialize;
use std::{path::PathBuf, net::{IpAddr, SocketAddr}};

/// Root configuration for ptproxy.
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Config {
	/// Mode of operation and peer + upstream parameters.
	/// <br> **Required**
	pub general: GeneralConfig,

	/// TLS settings for the QUIC endpoint.
	/// <br> **Required**
	pub tls: TlsConfig,

	/// Transport parameters for the QUIC session with the peer.
	/// <br> **Optional**
	#[serde(default)]
	pub transport: TransportConfig,

	/// Limits, timeouts, intervals and other parameters that affect operation of ptproxy.
	/// <br> **Optional**
	#[serde(default)]
	pub system: SystemConfig,
}

/// Parameters describing general proxy operation: mode, and connection details
/// for the other peer and the source / target for HTTP/1.1 requests.
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct GeneralConfig {
	/// Whether to connect to the target peer (`Client`), or accept connections from it (`Server`).
	/// <br> **Required**
	pub mode: PeerMode,

	/// Hostname to match in the other peer's certificate.
	/// <br> **Required**
	pub peer_hostname: String,

	/// UDP port where the QUIC server listens. In server mode, this determines the port to bind the socket at;
	/// in client mode, this determines the port to connect to.
	/// <br> **Default:** 20010
	#[serde(default = "default_quic_port")]
	pub quic_port: u16,

	/// Address to bind the QUIC socket at (valid in both server and client mode).
	/// <br> **Default:** `"::"` (IPv6 wildcard address)
	#[serde(default = "default_bind_address")]
	pub bind_address: IpAddr,

	/// Only valid in client mode: overrides the address to connect to the peer over QUIC.
	/// <br> **Default:** uses the value of `peer_hostname`
	pub connect_address: Option<String>,

	/// Only valid in client mode: socket address to bind the listening HTTP/1.1 endpoint at.
	/// <br> **Default:** `[::]:20080`
	pub http_bind_address: Option<SocketAddr>,

	/// Only valid in server mode: socket address to send HTTP/1.1 requests (received from the peer) to.
	/// <br> **Required**
	pub http_connect_address: Option<String>,
}

#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum PeerMode {
	Client,
	Server,
}

fn default_quic_port() -> u16 {
	20010
}

fn default_bind_address() -> IpAddr {
	"::".parse().unwrap()
}

pub fn default_http_bind_address() -> SocketAddr {
	"[::]:20080".parse().unwrap()
}

/// TLS identity settings for the QUIC endpoint.
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct TlsConfig {
	/// Trusted root CA certificates to verify the certificate of the peer against.
	/// <br> **Default:** use system root CA store
	pub ca: Option<PathBuf>,

	/// Skip verification of the peer certificate. **Dangerous.**
	/// <br> **Default:** false
	#[serde(default)]
	pub skip_verify: bool,

	/// Path to certificate to present to peers.
	/// <br> **Required**
	pub cert: PathBuf,

	/// Path to certificate's private key.
	/// <br> **Required**
	pub key: PathBuf,
}

/// Configuration of the [transport configuration](`quinn::TransportConfig`) of the connection with the peer.
/// These parameters are usually kept identical on both sides.
#[derive(Deserialize, Debug, Default)]
#[serde(deny_unknown_fields)]
pub struct TransportConfig {
	/// Maximum duration of inactivity to accept before considering the connection dead, in milliseconds.
	/// The true idle timeout is the minimum of this and the peer’s own max idle timeout.
	/// See [`quinn::TransportConfig::max_idle_timeout`].
	/// <br> **Default:** 5000
	pub max_idle_timeout: Option<u64>,

	/// Period of inactivity before sending a keep-alive packet, in milliseconds.
	/// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
	/// See [`quinn::TransportConfig::keep_alive_interval`].
	/// <br> **Default:** 2000
	pub keep_alive_interval: Option<u64>,

	/// Initial estimate of RTT with the peer, in milliseconds.
	/// This is the value used before an RTT sample is taken.
	/// See [`quinn::TransportConfig::initial_rtt`].
	/// <br> **Default:** see quinn documentation (spec default)
	pub initial_rtt: Option<u64>,

	/// **Flow control:** Maximum number of HTTP streams (requests) that may be open concurrently at any point in time.
	/// [`quinn::TransportConfig::max_concurrent_bidi_streams`] is set to this value (for servers) or to zero (for clients).
	/// <br> **Default:** 100
	pub max_concurrent_http_streams: Option<u32>,

	/// **Flow control:** Maximum data the peer may transmit without acknowledgement on any one stream before becoming blocked, in bytes.
	/// See [`quinn::TransportConfig::stream_receive_window`].
	/// <br> **Default:** 1MB
	pub stream_receive_window: Option<u64>,

	/// **Congestion control:** Size of the initial congestion window, in bytes.
	/// See [`quinn::congestion::CubicConfig::initial_window`].
	/// <br> **Default:** 14720 (spec default)
	pub initial_window: Option<u64>,

	/// **Flow control:** Maximum data the peer may transmit across all streams of a connection before becoming blocked, in bytes.
	/// See [`quinn::TransportConfig::receive_window`].
	/// <br> **Default:** `initial_window`
	pub receive_window: Option<u64>,

	/// **Flow control:** Maximum data to transmit to a peer without acknowledgment, in bytes.
	/// See [`quinn::TransportConfig::send_window`].
	/// <br> **Default:** `initial_window`
	pub send_window: Option<u64>,

	/// **OS network:** Size of the OS's receive buffer for the UDP socket (`SO_RCVBUF` option), in bytes.
	/// See [`socket2::Socket::set_recv_buffer_size`].
	/// <br> **Default:** OS default
	pub socket_receive_buffer_size: Option<usize>,

	/// **OS network:** Size of the OS's send buffer for the UDP socket (`SO_SNDBUF` option), in bytes.
	/// See [`socket2::Socket::set_send_buffer_size`].
	/// <br> **Default:** OS default
	pub socket_send_buffer_size: Option<usize>,

	/// **Congestion control:** Algorithm to use for the congestion controller.
	/// <br> **Default:** Cubic
	#[serde(default)]
	pub congestion_algorithm: CongestionAlgorithm,
}

#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum CongestionAlgorithm {
	/// See [`quinn::congestion::Bbr`].
	Bbr,
	/// See [`quinn::congestion::Cubic`].
	Cubic,
	/// See [`quinn::congestion::NewReno`].
	NewReno,
}

impl Default for CongestionAlgorithm {
	fn default() -> Self {
		CongestionAlgorithm::Cubic
	}
}

pub fn default_max_idle_timeout() -> u64 {
	5000
}

pub fn default_keep_alive_interval() -> u64 {
	2000
}

pub fn default_max_concurrent_http_streams() -> u32 {
	100
}

pub fn default_stream_receive_window() -> u64 {
	1_000_000
}

/// Limits, timeouts, intervals and other parameters that affect operation of ptproxy. This includes
/// proxy behavior, integration with the service manager, and interaction with HTTP/1.1 upstreams / downstreams.
#[derive(Deserialize, Debug, Default)]
#[serde(deny_unknown_fields)]
pub struct SystemConfig {
	/// Time to wait since last QUIC connection \[attempt\] failed before attempting a new connection, in milliseconds.
	/// Only used in client mode.
	/// <br> **Default:** 1000
	pub connect_interval: Option<u64>,

	/// Whether to enable the `TCP_NODELAY` option on incoming (client mode) or outgoing (server mode) HTTP/1.1 sockets.
	/// This helps avoid extra latency introduced by delayed ACKs, for example.
	/// <br> **Default:** true
	pub tcp_nodelay: Option<bool>,

	/// If enabled and service manager integration is in use, ptproxy will wait for the first connection attempt
	/// to finish (successfully or not) before sending signalling the service as *ready*. This gives a best-effort
	/// opportunity for the tunnel to establish before starting dependencies, for example.
	/// Only used in client mode.
	/// <br> **Default:** true
	pub wait_for_first_attempt: Option<bool>,

	/// If service manager integration is in use, and this service has been requested to send periodic keepalives,
	/// the watchdog timeout limit is divided by this factor to determine the interval at which to send them.
	/// <br> **Default:** 1.5
	pub watchdog_factor: Option<f32>,

	/// If enabled, a `Forwarded` header will be appended to the request before forwarding it.
	/// <br> **Default:** false
	pub add_forwarded: Option<bool>,
}

pub fn default_connect_interval() -> u64 {
	1000
}

pub fn default_tcp_nodelay() -> bool {
	true
}

pub fn default_wait_for_first_attempt() -> bool {
	true
}

pub fn default_watchdog_factor() -> f32 {
	1.5
}

pub fn default_add_forwarded() -> bool {
	false
}