1use std::collections::VecDeque;
4use std::fmt;
5use std::net::{SocketAddr, UdpSocket};
6
7use log::{debug, info};
8
9use crate::packet::{AllowedPackets, Packet};
10use crate::replay::ReplayProtection;
11use crate::token::{self, CHALLENGE_TOKEN_BYTES, ConnectToken};
12use crate::{
13 CONNECT_TOKEN_BYTES, Error, KEY_BYTES, Key, MAX_PACKET_BYTES, MAX_PAYLOAD_BYTES,
14 NUM_DISCONNECT_PACKETS, PACKET_QUEUE_SIZE, PACKET_SEND_RATE, socket,
15};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum ClientState {
25 ConnectTokenExpired,
27 InvalidConnectToken,
29 ConnectionTimedOut,
31 ConnectionResponseTimedOut,
33 ConnectionRequestTimedOut,
35 ConnectionDenied,
37 Disconnected,
39 SendingConnectionRequest,
41 SendingConnectionResponse,
43 Connected,
45}
46
47impl ClientState {
48 pub fn is_error(self) -> bool {
50 matches!(
51 self,
52 ClientState::ConnectTokenExpired
53 | ClientState::InvalidConnectToken
54 | ClientState::ConnectionTimedOut
55 | ClientState::ConnectionResponseTimedOut
56 | ClientState::ConnectionRequestTimedOut
57 | ClientState::ConnectionDenied
58 )
59 }
60
61 pub fn is_connecting(self) -> bool {
64 matches!(
65 self,
66 ClientState::SendingConnectionRequest | ClientState::SendingConnectionResponse
67 )
68 }
69
70 pub fn is_disconnected(self) -> bool {
73 self == ClientState::Disconnected || self.is_error()
74 }
75}
76
77impl fmt::Display for ClientState {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 let name = match self {
80 ClientState::ConnectTokenExpired => "connect token expired",
81 ClientState::InvalidConnectToken => "invalid connect token",
82 ClientState::ConnectionTimedOut => "connection timed out",
83 ClientState::ConnectionResponseTimedOut => "connection response timed out",
84 ClientState::ConnectionRequestTimedOut => "connection request timed out",
85 ClientState::ConnectionDenied => "connection denied",
86 ClientState::Disconnected => "disconnected",
87 ClientState::SendingConnectionRequest => "sending connection request",
88 ClientState::SendingConnectionResponse => "sending connection response",
89 ClientState::Connected => "connected",
90 };
91 f.write_str(name)
92 }
93}
94
95pub struct Client {
101 state: ClientState,
102 time: f64,
103 connect_start_time: f64,
104 last_packet_send_time: f64,
105 last_packet_receive_time: f64,
106 should_disconnect: Option<ClientState>,
107 sequence: u64,
108 client_index: usize,
109 max_clients: usize,
110 server_address_index: usize,
111 server_address: Option<SocketAddr>,
112 connect_token: Option<ConnectToken>,
113 socket_ipv4: Option<UdpSocket>,
114 socket_ipv6: Option<UdpSocket>,
115 primary_is_ipv4: bool,
116 write_packet_key: Key,
117 read_packet_key: Key,
118 replay_protection: ReplayProtection,
119 packet_receive_queue: VecDeque<(Vec<u8>, u64)>,
120 challenge_token_sequence: u64,
121 challenge_token_data: [u8; CHALLENGE_TOKEN_BYTES],
122}
123
124impl Client {
125 pub fn new(bind_address: SocketAddr, time: f64) -> Result<Self, Error> {
132 match bind_address {
133 SocketAddr::V4(_) => Self::create(Some(bind_address), None, true, time),
134 SocketAddr::V6(_) => Self::create(None, Some(bind_address), false, time),
135 }
136 }
137
138 pub fn new_dual(
141 bind_address_ipv4: SocketAddr,
142 bind_address_ipv6: SocketAddr,
143 time: f64,
144 ) -> Result<Self, Error> {
145 if !bind_address_ipv4.is_ipv4() || !bind_address_ipv6.is_ipv6() {
146 return Err(Error::Io(std::io::Error::new(
147 std::io::ErrorKind::InvalidInput,
148 "new_dual requires one IPv4 and one IPv6 bind address",
149 )));
150 }
151 Self::create(Some(bind_address_ipv4), Some(bind_address_ipv6), true, time)
152 }
153
154 fn create(
155 bind_address_ipv4: Option<SocketAddr>,
156 bind_address_ipv6: Option<SocketAddr>,
157 primary_is_ipv4: bool,
158 time: f64,
159 ) -> Result<Self, Error> {
160 let socket_ipv4 = bind_address_ipv4.map(socket::create_socket).transpose()?;
161 let socket_ipv6 = bind_address_ipv6.map(socket::create_socket).transpose()?;
162
163 let client = Self {
164 state: ClientState::Disconnected,
165 time,
166 connect_start_time: 0.0,
167 last_packet_send_time: -1000.0,
168 last_packet_receive_time: -1000.0,
169 should_disconnect: None,
170 sequence: 0,
171 client_index: 0,
172 max_clients: 0,
173 server_address_index: 0,
174 server_address: None,
175 connect_token: None,
176 socket_ipv4,
177 socket_ipv6,
178 primary_is_ipv4,
179 write_packet_key: [0; KEY_BYTES],
180 read_packet_key: [0; KEY_BYTES],
181 replay_protection: ReplayProtection::new(),
182 packet_receive_queue: VecDeque::new(),
183 challenge_token_sequence: 0,
184 challenge_token_data: [0; CHALLENGE_TOKEN_BYTES],
185 };
186
187 info!("client started on port {}", client.port());
188
189 Ok(client)
190 }
191
192 pub fn connect(&mut self, connect_token: &[u8; CONNECT_TOKEN_BYTES]) -> Result<(), Error> {
199 self.disconnect();
200
201 let connect_token = match ConnectToken::read(connect_token) {
202 Ok(connect_token) => connect_token,
203 Err(error) => {
204 self.set_state(ClientState::InvalidConnectToken);
205 return Err(error);
206 }
207 };
208
209 self.server_address_index = 0;
210 self.server_address = Some(connect_token.server_addresses[0]);
211
212 if connect_token.server_addresses.len() == 1 {
213 info!("client connecting to server {}", connect_token.server_addresses[0]);
214 } else {
215 info!(
216 "client connecting to server {} [1/{}]",
217 connect_token.server_addresses[0],
218 connect_token.server_addresses.len()
219 );
220 }
221
222 self.read_packet_key = connect_token.server_to_client_key;
223 self.write_packet_key = connect_token.client_to_server_key;
224 self.connect_token = Some(connect_token);
225
226 self.reset_before_next_connect();
227 self.set_state(ClientState::SendingConnectionRequest);
228
229 Ok(())
230 }
231
232 pub fn update(&mut self, time: f64) {
235 self.time = time;
236
237 self.receive_packets();
238 self.send_packets();
239
240 if self.state.is_connecting() {
241 let connect_token = self.connect_token.as_ref().unwrap();
242 let expire_seconds = connect_token.expire_timestamp - connect_token.create_timestamp;
243 if self.time - self.connect_start_time >= expire_seconds as f64 {
244 info!("client connect failed. connect token expired");
245 self.disconnect_internal(ClientState::ConnectTokenExpired, false);
246 return;
247 }
248 }
249
250 if let Some(disconnect_state) = self.should_disconnect {
251 debug!("client should disconnect -> {disconnect_state}");
252 if self.connect_to_next_server() {
253 return;
254 }
255 self.disconnect_internal(disconnect_state, false);
256 return;
257 }
258
259 let timeout_seconds =
260 self.connect_token.as_ref().map_or(0, |connect_token| connect_token.timeout_seconds);
261 let timed_out =
262 timeout_seconds > 0 && self.last_packet_receive_time + (timeout_seconds as f64) < time;
263 if !timed_out {
264 return;
265 }
266
267 match self.state {
268 ClientState::SendingConnectionRequest => {
269 info!("client connect failed. connection request timed out");
270 if !self.connect_to_next_server() {
271 self.disconnect_internal(ClientState::ConnectionRequestTimedOut, false);
272 }
273 }
274 ClientState::SendingConnectionResponse => {
275 info!("client connect failed. connection response timed out");
276 if !self.connect_to_next_server() {
277 self.disconnect_internal(ClientState::ConnectionResponseTimedOut, false);
278 }
279 }
280 ClientState::Connected => {
281 info!("client connection timed out");
282 self.disconnect_internal(ClientState::ConnectionTimedOut, false);
283 }
284 _ => {}
285 }
286 }
287
288 pub fn send_packet(&mut self, payload: &[u8]) -> Result<(), Error> {
291 if payload.is_empty() || payload.len() > MAX_PAYLOAD_BYTES {
292 return Err(Error::InvalidPayloadSize(payload.len()));
293 }
294
295 if self.state != ClientState::Connected {
296 return Ok(());
297 }
298
299 self.send_packet_to_server(&Packet::Payload(payload.to_vec()));
300
301 Ok(())
302 }
303
304 pub fn receive_packet(&mut self) -> Option<(Vec<u8>, u64)> {
307 self.packet_receive_queue.pop_front()
308 }
309
310 pub fn disconnect(&mut self) {
313 self.disconnect_internal(ClientState::Disconnected, true);
314 }
315
316 pub fn state(&self) -> ClientState {
318 self.state
319 }
320
321 pub fn client_index(&self) -> usize {
323 self.client_index
324 }
325
326 pub fn max_clients(&self) -> usize {
328 self.max_clients
329 }
330
331 pub fn next_packet_sequence(&self) -> u64 {
333 self.sequence
334 }
335
336 pub fn port(&self) -> u16 {
338 let socket = if self.primary_is_ipv4 {
339 self.socket_ipv4.as_ref()
340 } else {
341 self.socket_ipv6.as_ref()
342 };
343 socket.and_then(|socket| socket.local_addr().ok()).map_or(0, |address| address.port())
344 }
345
346 pub fn server_address(&self) -> Option<SocketAddr> {
348 self.server_address
349 }
350
351 fn set_state(&mut self, state: ClientState) {
354 debug!("client changed state from '{}' to '{}'", self.state, state);
355 self.state = state;
356 }
357
358 fn reset_before_next_connect(&mut self) {
359 self.connect_start_time = self.time;
360 self.last_packet_send_time = self.time - 1.0;
361 self.last_packet_receive_time = self.time;
362 self.should_disconnect = None;
363 self.challenge_token_sequence = 0;
364 self.challenge_token_data = [0; CHALLENGE_TOKEN_BYTES];
365 self.replay_protection.reset();
366 }
367
368 fn reset_connection_data(&mut self, state: ClientState) {
369 self.sequence = 0;
370 self.client_index = 0;
371 self.max_clients = 0;
372 self.connect_start_time = 0.0;
373 self.server_address_index = 0;
374 self.server_address = None;
375 self.connect_token = None;
376 self.write_packet_key = [0; KEY_BYTES];
377 self.read_packet_key = [0; KEY_BYTES];
378 self.set_state(state);
379 self.reset_before_next_connect();
380 self.packet_receive_queue.clear();
381 }
382
383 fn disconnect_internal(
384 &mut self,
385 destination_state: ClientState,
386 send_disconnect_packets: bool,
387 ) {
388 debug_assert!(destination_state.is_disconnected());
389
390 if self.state.is_disconnected() || self.state == destination_state {
391 return;
392 }
393
394 info!("client disconnected");
395
396 if send_disconnect_packets {
397 debug!("client sent disconnect packets to server");
398 for _ in 0..NUM_DISCONNECT_PACKETS {
399 self.send_packet_to_server(&Packet::Disconnect);
400 }
401 }
402
403 self.reset_connection_data(destination_state);
404 }
405
406 fn connect_to_next_server(&mut self) -> bool {
407 let Some(connect_token) = self.connect_token.as_ref() else {
408 return false;
409 };
410 let num_server_addresses = connect_token.server_addresses.len();
411
412 if self.server_address_index + 1 >= num_server_addresses {
413 debug!("client has no more servers to connect to");
414 return false;
415 }
416
417 self.server_address_index += 1;
418 let server_address = connect_token.server_addresses[self.server_address_index];
419 self.server_address = Some(server_address);
420
421 self.reset_before_next_connect();
422
423 info!(
424 "client connecting to next server {} [{}/{}]",
425 server_address,
426 self.server_address_index + 1,
427 num_server_addresses
428 );
429
430 self.set_state(ClientState::SendingConnectionRequest);
431
432 true
433 }
434
435 fn send_packets(&mut self) {
436 if self.last_packet_send_time + 1.0 / PACKET_SEND_RATE >= self.time {
437 return;
438 }
439
440 match self.state {
441 ClientState::SendingConnectionRequest => {
442 debug!("client sent connection request packet to server");
443 let connect_token = self.connect_token.as_ref().unwrap();
444 let packet = Packet::Request {
445 protocol_id: connect_token.protocol_id,
446 expire_timestamp: connect_token.expire_timestamp,
447 nonce: connect_token.nonce,
448 private_data: connect_token.private_data.clone(),
449 };
450 self.send_packet_to_server(&packet);
451 }
452 ClientState::SendingConnectionResponse => {
453 debug!("client sent connection response packet to server");
454 let packet = Packet::Response {
455 challenge_token_sequence: self.challenge_token_sequence,
456 challenge_token_data: self.challenge_token_data,
457 };
458 self.send_packet_to_server(&packet);
459 }
460 ClientState::Connected => {
461 debug!("client sent connection keep alive packet to server");
462 let packet = Packet::KeepAlive { client_index: 0, max_clients: 0 };
463 self.send_packet_to_server(&packet);
464 }
465 _ => {}
466 }
467 }
468
469 fn send_packet_to_server(&mut self, packet: &Packet) {
470 let Some(server_address) = self.server_address else {
471 return;
472 };
473 let protocol_id =
474 self.connect_token.as_ref().map_or(0, |connect_token| connect_token.protocol_id);
475
476 let mut packet_data = [0u8; MAX_PACKET_BYTES];
477 let packet_bytes = match crate::packet::write_packet(
478 packet,
479 &mut packet_data,
480 self.sequence,
481 &self.write_packet_key,
482 protocol_id,
483 ) {
484 Ok(packet_bytes) => packet_bytes,
485 Err(_) => return,
486 };
487 self.sequence += 1;
488
489 let socket = match server_address {
490 SocketAddr::V4(_) => self.socket_ipv4.as_ref(),
491 SocketAddr::V6(_) => self.socket_ipv6.as_ref(),
492 };
493 match socket {
494 Some(socket) => {
495 let _ = socket.send_to(&packet_data[..packet_bytes], server_address);
496 }
497 None => {
498 debug!("client has no socket for server address family: {server_address}");
499 }
500 }
501
502 self.last_packet_send_time = self.time;
503 }
504
505 fn receive_packets(&mut self) {
506 let mut packet_data = [0u8; MAX_PACKET_BYTES];
507 loop {
508 let socket = match self.server_address {
512 Some(SocketAddr::V4(_)) => self.socket_ipv4.as_ref(),
513 Some(SocketAddr::V6(_)) => self.socket_ipv6.as_ref(),
514 None => None,
515 };
516 let Some(socket) = socket else {
517 return;
518 };
519 let Some((packet_bytes, from)) = socket::receive_packet(socket, &mut packet_data)
520 else {
521 return;
522 };
523 self.process_packet(from, &mut packet_data[..packet_bytes]);
524 }
525 }
526
527 fn process_packet(&mut self, from: SocketAddr, packet_data: &mut [u8]) {
528 let Some(connect_token) = self.connect_token.as_ref() else {
529 return;
530 };
531
532 let Some((packet, sequence)) = crate::packet::read_packet(
533 packet_data,
534 Some(&self.read_packet_key),
535 connect_token.protocol_id,
536 token::unix_timestamp(),
537 None,
538 AllowedPackets::CLIENT,
539 Some(&mut self.replay_protection),
540 ) else {
541 return;
542 };
543
544 if Some(from) != self.server_address {
545 return;
546 }
547
548 match packet {
549 Packet::Denied => {
550 if self.state == ClientState::SendingConnectionRequest
551 || self.state == ClientState::SendingConnectionResponse
552 {
553 self.should_disconnect = Some(ClientState::ConnectionDenied);
554 self.last_packet_receive_time = self.time;
555 }
556 }
557
558 Packet::Challenge { challenge_token_sequence, challenge_token_data } => {
559 if self.state == ClientState::SendingConnectionRequest {
560 debug!("client received connection challenge packet from server");
561 self.challenge_token_sequence = challenge_token_sequence;
562 self.challenge_token_data = challenge_token_data;
563 self.last_packet_receive_time = self.time;
564 self.set_state(ClientState::SendingConnectionResponse);
565 }
566 }
567
568 Packet::KeepAlive { client_index, max_clients } => match self.state {
569 ClientState::Connected => {
570 debug!("client received connection keep alive packet from server");
571 self.last_packet_receive_time = self.time;
572 }
573 ClientState::SendingConnectionResponse => {
574 debug!("client received connection keep alive packet from server");
575 self.last_packet_receive_time = self.time;
576 self.client_index = client_index as usize;
577 self.max_clients = max_clients as usize;
578 self.set_state(ClientState::Connected);
579 info!("client connected to server");
580 }
581 _ => {}
582 },
583
584 Packet::Payload(payload) => {
585 if self.state == ClientState::Connected {
586 debug!("client received connection payload packet from server");
587 if self.packet_receive_queue.len() < PACKET_QUEUE_SIZE {
588 self.packet_receive_queue.push_back((payload, sequence));
589 }
590 self.last_packet_receive_time = self.time;
591 }
592 }
593
594 Packet::Disconnect => {
595 if self.state == ClientState::Connected {
596 debug!("client received disconnect packet from server");
597 self.should_disconnect = Some(ClientState::Disconnected);
598 self.last_packet_receive_time = self.time;
599 }
600 }
601
602 Packet::Request { .. } | Packet::Response { .. } => unreachable!(),
603 }
604 }
605}
606
607impl Drop for Client {
608 fn drop(&mut self) {
609 self.disconnect();
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[test]
618 fn state_predicates() {
619 let error_states = [
620 ClientState::ConnectTokenExpired,
621 ClientState::InvalidConnectToken,
622 ClientState::ConnectionTimedOut,
623 ClientState::ConnectionResponseTimedOut,
624 ClientState::ConnectionRequestTimedOut,
625 ClientState::ConnectionDenied,
626 ];
627 for state in error_states {
628 assert!(state.is_error());
629 assert!(state.is_disconnected());
630 assert!(!state.is_connecting());
631 }
632
633 assert!(!ClientState::Disconnected.is_error());
634 assert!(ClientState::Disconnected.is_disconnected());
635 assert!(!ClientState::Disconnected.is_connecting());
636
637 for state in [ClientState::SendingConnectionRequest, ClientState::SendingConnectionResponse]
638 {
639 assert!(state.is_connecting());
640 assert!(!state.is_error());
641 assert!(!state.is_disconnected());
642 }
643
644 assert!(!ClientState::Connected.is_error());
645 assert!(!ClientState::Connected.is_disconnected());
646 assert!(!ClientState::Connected.is_connecting());
647 }
648
649 #[test]
650 fn invalid_connect_token() {
651 let mut client = Client::new("127.0.0.1:0".parse().unwrap(), 0.0).unwrap();
652 let connect_token = [0u8; CONNECT_TOKEN_BYTES];
653 assert!(client.connect(&connect_token).is_err());
654 assert_eq!(client.state(), ClientState::InvalidConnectToken);
655 }
656}