|
4 | 4 | use super::*;
|
5 | 5 | use s2n_codec::DecoderBufferMut;
|
6 | 6 | use s2n_quic_core::{
|
7 |
| - event::api::{DatagramDropReason, Subject}, |
| 7 | + event::api::{DatagramDropReason, MigrationDenyReason, Subject}, |
8 | 8 | packet::interceptor::{Datagram, Interceptor},
|
9 | 9 | path::{LocalAddress, RemoteAddress},
|
10 | 10 | };
|
@@ -373,3 +373,77 @@ fn rebind_ipv4_mapped_before_handshake_confirmed() {
|
373 | 373 | }
|
374 | 374 | }
|
375 | 375 | }
|
| 376 | + |
| 377 | +/// Rebinds to a port after a specified number of packets |
| 378 | +struct RebindToPort { |
| 379 | + port: u16, |
| 380 | + after: usize, |
| 381 | +} |
| 382 | + |
| 383 | +impl Interceptor for RebindToPort { |
| 384 | + fn intercept_rx_remote_address(&mut self, _subject: &Subject, addr: &mut RemoteAddress) { |
| 385 | + if self.after == 0 { |
| 386 | + addr.set_port(self.port); |
| 387 | + } |
| 388 | + } |
| 389 | + |
| 390 | + fn intercept_rx_datagram<'a>( |
| 391 | + &mut self, |
| 392 | + _subject: &Subject, |
| 393 | + _datagram: &Datagram, |
| 394 | + payload: DecoderBufferMut<'a>, |
| 395 | + ) -> DecoderBufferMut<'a> { |
| 396 | + self.after = self.after.saturating_sub(1); |
| 397 | + payload |
| 398 | + } |
| 399 | +} |
| 400 | + |
| 401 | +/// Ensures that a blocked port is not migrated to |
| 402 | +#[test] |
| 403 | +fn rebind_blocked_port() { |
| 404 | + let model = Model::default(); |
| 405 | + let subscriber = recorder::DatagramDropped::new(); |
| 406 | + let datagram_dropped_events = subscriber.events(); |
| 407 | + |
| 408 | + test(model, move |handle| { |
| 409 | + let server = Server::builder() |
| 410 | + .with_io(handle.builder().build()?)? |
| 411 | + .with_tls(SERVER_CERTS)? |
| 412 | + .with_event((tracing_events(), subscriber))? |
| 413 | + .with_random(Random::with_seed(456))? |
| 414 | + .with_packet_interceptor(RebindToPort { port: 53, after: 2 })? |
| 415 | + .start()?; |
| 416 | + |
| 417 | + let client = Client::builder() |
| 418 | + .with_io(handle.builder().build()?)? |
| 419 | + .with_tls(certificates::CERT_PEM)? |
| 420 | + .with_event(tracing_events())? |
| 421 | + .with_random(Random::with_seed(456))? |
| 422 | + .start()?; |
| 423 | + |
| 424 | + let addr = start_server(server)?; |
| 425 | + |
| 426 | + primary::spawn(async move { |
| 427 | + let mut connection = client |
| 428 | + .connect(Connect::new(addr).with_server_name("localhost")) |
| 429 | + .await |
| 430 | + .unwrap(); |
| 431 | + let mut stream = connection.open_bidirectional_stream().await.unwrap(); |
| 432 | + let _ = stream.send(Bytes::from_static(b"hello")).await; |
| 433 | + let _ = stream.finish(); |
| 434 | + let _ = stream.receive().await; |
| 435 | + }); |
| 436 | + |
| 437 | + Ok(addr) |
| 438 | + }) |
| 439 | + .unwrap(); |
| 440 | + |
| 441 | + let datagram_dropped_events = datagram_dropped_events.lock().unwrap(); |
| 442 | + |
| 443 | + assert!(!datagram_dropped_events.is_empty()); |
| 444 | + for event in datagram_dropped_events.iter() { |
| 445 | + if let DatagramDropReason::RejectedConnectionMigration { reason, .. } = &event.reason { |
| 446 | + assert!(matches!(reason, MigrationDenyReason::BlockedPort { .. })); |
| 447 | + } |
| 448 | + } |
| 449 | +} |
0 commit comments