1use std::{
2 marker::PhantomData,
3 net::{SocketAddr, SocketAddrV4, SocketAddrV6},
4 os::fd::AsRawFd,
5 sync::Arc,
6};
7
8use tokio::io::{unix::AsyncFd, Interest};
9
10use crate::{
11 control_message::{ControlMessage, MessageQueue, EXPECTED_MAX_CMSG_SIZE},
12 interface::InterfaceName,
13 networkaddress::{sealed::PrivateToken, MulticastJoinable, NetworkAddress},
14 raw_socket::RawSocket,
15};
16
17#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
18mod fallback;
19#[cfg(target_os = "freebsd")]
20mod freebsd;
21#[cfg(target_os = "linux")]
22mod linux;
23#[cfg(target_os = "macos")]
24mod macos;
25
26#[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "macos")))]
27use self::fallback::*;
28#[cfg(target_os = "freebsd")]
29use self::freebsd::*;
30#[cfg(target_os = "linux")]
31pub use self::linux::*;
32#[cfg(target_os = "macos")]
33use self::macos::*;
34
35#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
36pub struct TimestampData {
37 pub timestamp_mode: InterfaceTimestampMode,
41 pub hardware: Option<Timestamp>,
43 pub software: Option<Timestamp>,
45}
46
47impl TimestampData {
48 fn empty(timestamp_mode: InterfaceTimestampMode) -> Self {
50 Self::default().with_timestamp_mode(timestamp_mode)
51 }
52
53 pub fn selected_timestamp(self) -> Option<Timestamp> {
58 use InterfaceTimestampMode::*;
59
60 match self.timestamp_mode {
61 SoftwareAll | SoftwareRecv => self.software,
62 HardwareAll | HardwareRecv | HardwarePTPAll | HardwarePTPRecv => self.hardware,
63 None => Option::None,
64 }
65 }
66
67 fn with_timestamp_mode(self, timestamp_mode: InterfaceTimestampMode) -> Self {
69 Self {
70 timestamp_mode,
71 ..self
72 }
73 }
74}
75
76#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Default)]
77pub struct Timestamp {
78 pub seconds: i64,
79 pub nanos: u32,
80}
81
82impl Timestamp {
83 #[cfg_attr(target_os = "macos", allow(unused))] pub(crate) fn from_timespec(timespec: libc::timespec) -> Self {
85 Self {
86 seconds: timespec.tv_sec as _,
87 nanos: timespec.tv_nsec as _,
88 }
89 }
90
91 pub(crate) fn from_timeval(timeval: libc::timeval) -> Self {
92 Self {
93 seconds: timeval.tv_sec as _,
94 nanos: (1000 * timeval.tv_usec) as _,
95 }
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
100pub enum GeneralTimestampMode {
101 SoftwareAll,
102 SoftwareRecv,
103 #[default]
104 None,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
108pub enum InterfaceTimestampMode {
109 HardwareAll,
110 HardwareRecv,
111 HardwarePTPAll,
112 HardwarePTPRecv,
113 SoftwareAll,
114 SoftwareRecv,
115 #[default]
116 None,
117}
118
119impl From<GeneralTimestampMode> for InterfaceTimestampMode {
120 fn from(value: GeneralTimestampMode) -> Self {
121 match value {
122 GeneralTimestampMode::SoftwareAll => InterfaceTimestampMode::SoftwareAll,
123 GeneralTimestampMode::SoftwareRecv => InterfaceTimestampMode::SoftwareRecv,
124 GeneralTimestampMode::None => InterfaceTimestampMode::None,
125 }
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
130pub struct RecvResult<A> {
131 pub bytes_read: usize,
132 pub remote_addr: A,
133 pub local_addr: A,
134 pub timestamp_data: TimestampData,
135}
136
137#[derive(Debug)]
138pub struct Socket<A, S> {
139 timestamp_mode: InterfaceTimestampMode,
140 socket: Arc<AsyncFd<RawSocket>>,
142 #[cfg(target_os = "linux")]
143 send_counter: std::sync::Mutex<u32>,
144 local_addr: A,
145 _state: PhantomData<S>,
146}
147
148#[non_exhaustive]
149pub struct Open;
150#[non_exhaustive]
151pub struct Connected;
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
154pub struct SendTimestampToken(u32);
155
156impl<A: NetworkAddress, S> Socket<A, S> {
157 pub fn local_addr(&self) -> A {
158 self.local_addr
159 }
160
161 fn inner_recv(&self, buf: &mut [u8], socket: &RawSocket) -> std::io::Result<RecvResult<A>> {
162 let mut control_buf = [0; EXPECTED_MAX_CMSG_SIZE];
163
164 let (bytes_read, control_messages, remote_address) =
166 socket.receive_message(buf, &mut control_buf, MessageQueue::Normal)?;
167
168 let mut timestamp_data = TimestampData::empty(self.timestamp_mode);
169 let mut local_addr = self.local_addr;
170
171 for msg in control_messages {
174 match msg {
175 ControlMessage::Timestamping { software, hardware } => {
176 tracing::trace!("Timestamps: {:?} {:?}", software, hardware);
177
178 timestamp_data.software = timestamp_data.software.or(software);
180 timestamp_data.hardware = timestamp_data.hardware.or(hardware);
181 }
182
183 #[cfg(target_os = "linux")]
184 ControlMessage::ReceiveError(error) => {
185 tracing::debug!(
186 "unexpected error control message on receive: {}",
187 error.ee_errno
188 );
189 }
190
191 ControlMessage::DestinationIp(addr) => {
192 if let Some(addr) = A::from_ip_and_port(addr, self.local_addr.port()) {
193 local_addr = addr;
194 }
195 }
196
197 ControlMessage::Other(msg) => {
198 tracing::debug!(
199 "unexpected control message on receive: {} {}",
200 msg.cmsg_level,
201 msg.cmsg_type,
202 );
203 }
204 }
205 }
206
207 let remote_addr =
208 A::from_sockaddr(remote_address, PrivateToken).ok_or(std::io::ErrorKind::Other)?;
209
210 Ok(RecvResult {
211 bytes_read,
212 remote_addr,
213 local_addr,
214 timestamp_data,
215 })
216 }
217
218 pub fn poll_recv(
224 &self,
225 buf: &mut [u8],
226 cx: &mut std::task::Context<'_>,
227 ) -> std::task::Poll<std::io::Result<RecvResult<A>>> {
228 match self.socket.poll_read_ready(cx) {
229 std::task::Poll::Ready(Ok(mut guard)) => {
230 match guard.try_io(|inner| self.inner_recv(buf, inner.get_ref())) {
231 Ok(result) => std::task::Poll::Ready(result),
232 Err(_) => std::task::Poll::Pending,
233 }
234 }
235 std::task::Poll::Ready(Err(e)) => std::task::Poll::Ready(Err(e)),
236 std::task::Poll::Pending => std::task::Poll::Pending,
237 }
238 }
239
240 pub async fn recv(&self, buf: &mut [u8]) -> std::io::Result<RecvResult<A>> {
241 self.socket
242 .async_io(Interest::READABLE, |socket| self.inner_recv(buf, socket))
243 .await
244 }
245
246 fn send_inner(
247 &self,
248 send_call: impl FnOnce() -> std::io::Result<()>,
249 ) -> std::io::Result<Option<SendTimestampToken>> {
250 if matches!(
251 self.timestamp_mode,
252 InterfaceTimestampMode::HardwarePTPAll | InterfaceTimestampMode::SoftwareAll
253 ) {
254 #[cfg(target_os = "linux")]
255 {
256 let mut counter = self.send_counter.lock().unwrap();
257 send_call()?;
258 let token = SendTimestampToken(*counter);
259 *counter = counter.wrapping_add(1);
260 Ok(Some(token))
261 }
262
263 #[cfg(not(target_os = "linux"))]
264 {
265 unreachable!("Should not be able to create send timestamping sockets on platforms other than linux")
266 }
267 } else {
268 send_call()?;
269 Ok(None)
270 }
271 }
272
273 pub fn get_send_timestamp(
280 &self,
281 ) -> impl std::future::Future<Output = std::io::Result<(SendTimestampToken, TimestampData)>>
282 + Send
283 + Sync
284 + 'static {
285 let timestamp_mode = self.timestamp_mode;
286 let socket = self.socket.clone();
287 async move {
288 if matches!(
289 timestamp_mode,
290 InterfaceTimestampMode::HardwarePTPAll | InterfaceTimestampMode::SoftwareAll
291 ) {
292 #[cfg(target_os = "linux")]
293 {
294 let (counter, timestamp) = Self::fetch_send_timestamp(socket).await?;
295 Ok((
296 SendTimestampToken(counter),
297 timestamp.with_timestamp_mode(timestamp_mode),
298 ))
299 }
300
301 #[cfg(not(target_os = "linux"))]
302 {
303 let _ = socket;
304 unreachable!("Should not be able to create send timestamping sockets on platforms other than linux")
305 }
306 } else {
307 Err(std::io::ErrorKind::Unsupported.into())
308 }
309 }
310 }
311
312 async fn send_timestamp_for_transmit(
319 &mut self,
320 token: SendTimestampToken,
321 ) -> std::io::Result<TimestampData> {
322 use std::time::Duration;
323
324 const TIMEOUT: Duration = Duration::from_millis(200);
325
326 tokio::time::timeout(TIMEOUT, async {
327 loop {
328 let (cur_token, timestamp_data) = self.get_send_timestamp().await?;
329 if cur_token == token {
330 return Ok(timestamp_data.with_timestamp_mode(self.timestamp_mode));
331 }
332 }
333 })
334 .await
335 .unwrap_or(Ok(TimestampData::empty(self.timestamp_mode)))
336 }
337}
338
339impl<A: NetworkAddress> Socket<A, Open> {
340 pub fn poll_send_to(
346 &self,
347 buf: &[u8],
348 addr: A,
349 cx: &mut std::task::Context<'_>,
350 ) -> std::task::Poll<std::io::Result<Option<SendTimestampToken>>> {
351 let addr = addr.to_sockaddr(PrivateToken);
352
353 match self.socket.poll_write_ready(cx) {
354 std::task::Poll::Ready(Ok(mut guard)) => {
355 match guard.try_io(|inner| self.send_inner(|| inner.get_ref().send_to(buf, addr))) {
356 Ok(result) => std::task::Poll::Ready(result),
357 Err(_) => std::task::Poll::Pending,
358 }
359 }
360 std::task::Poll::Ready(Err(e)) => std::task::Poll::Ready(Err(e)),
361 std::task::Poll::Pending => std::task::Poll::Pending,
362 }
363 }
364
365 pub async fn send_to(&mut self, buf: &[u8], addr: A) -> std::io::Result<TimestampData> {
371 let addr = addr.to_sockaddr(PrivateToken);
372
373 if let Some(token) = self
374 .socket
375 .async_io(Interest::WRITABLE, |socket| {
376 self.send_inner(|| socket.send_to(buf, addr))
377 })
378 .await?
379 {
380 self.send_timestamp_for_transmit(token).await
381 } else {
382 Ok(TimestampData::empty(self.timestamp_mode))
383 }
384 }
385
386 pub fn poll_send_from_to(
392 &self,
393 buf: &[u8],
394 from: A,
395 to: A,
396 cx: &mut std::task::Context<'_>,
397 ) -> std::task::Poll<std::io::Result<Option<SendTimestampToken>>> {
398 let from = from.to_sockaddr(PrivateToken);
399 let to = to.to_sockaddr(PrivateToken);
400
401 match self.socket.poll_write_ready(cx) {
402 std::task::Poll::Ready(Ok(mut guard)) => match guard
403 .try_io(|inner| self.send_inner(|| inner.get_ref().send_from_to(buf, from, to)))
404 {
405 Ok(result) => std::task::Poll::Ready(result),
406 Err(_) => std::task::Poll::Pending,
407 },
408 std::task::Poll::Ready(Err(e)) => std::task::Poll::Ready(Err(e)),
409 std::task::Poll::Pending => std::task::Poll::Pending,
410 }
411 }
412
413 pub async fn send_from_to(
419 &mut self,
420 buf: &[u8],
421 from: A,
422 to: A,
423 ) -> std::io::Result<TimestampData> {
424 let from = from.to_sockaddr(PrivateToken);
425 let to = to.to_sockaddr(PrivateToken);
426
427 if let Some(token) = self
428 .socket
429 .async_io(Interest::WRITABLE, |socket| {
430 self.send_inner(|| socket.send_from_to(buf, from, to))
431 })
432 .await?
433 {
434 self.send_timestamp_for_transmit(token).await
435 } else {
436 Ok(TimestampData::empty(self.timestamp_mode))
437 }
438 }
439
440 pub fn connect(self, addr: A) -> std::io::Result<Socket<A, Connected>> {
441 let addr = addr.to_sockaddr(PrivateToken);
442 self.socket.get_ref().connect(addr)?;
443 Ok(Socket {
444 timestamp_mode: self.timestamp_mode,
445 socket: self.socket,
446 #[cfg(target_os = "linux")]
447 send_counter: self.send_counter,
448 local_addr: self.local_addr,
449 _state: PhantomData,
450 })
451 }
452}
453
454impl<A: NetworkAddress> Socket<A, Connected> {
455 pub fn peer_addr(&self) -> std::io::Result<A> {
456 let addr = self.socket.get_ref().getpeername()?;
457 A::from_sockaddr(addr, PrivateToken).ok_or_else(|| std::io::ErrorKind::Other.into())
458 }
459
460 pub fn poll_send(
466 &self,
467 buf: &[u8],
468 cx: &mut std::task::Context<'_>,
469 ) -> std::task::Poll<std::io::Result<Option<SendTimestampToken>>> {
470 match self.socket.poll_write_ready(cx) {
471 std::task::Poll::Ready(Ok(mut guard)) => {
472 match guard.try_io(|inner| self.send_inner(|| inner.get_ref().send(buf))) {
473 Ok(result) => std::task::Poll::Ready(result),
474 Err(_) => std::task::Poll::Pending,
475 }
476 }
477 std::task::Poll::Ready(Err(e)) => std::task::Poll::Ready(Err(e)),
478 std::task::Poll::Pending => std::task::Poll::Pending,
479 }
480 }
481
482 pub async fn send(&mut self, buf: &[u8]) -> std::io::Result<TimestampData> {
488 if let Some(token) = self
489 .socket
490 .async_io(Interest::WRITABLE, |socket| {
491 self.send_inner(|| socket.send(buf))
492 })
493 .await?
494 {
495 self.send_timestamp_for_transmit(token).await
496 } else {
497 Ok(TimestampData::empty(self.timestamp_mode))
498 }
499 }
500
501 pub fn poll_send_from(
507 &self,
508 buf: &[u8],
509 from: A,
510 cx: &mut std::task::Context<'_>,
511 ) -> std::task::Poll<std::io::Result<Option<SendTimestampToken>>> {
512 let from = from.to_sockaddr(PrivateToken);
513
514 match self.socket.poll_write_ready(cx) {
515 std::task::Poll::Ready(Ok(mut guard)) => match guard
516 .try_io(|inner| self.send_inner(|| inner.get_ref().send_from(buf, from)))
517 {
518 Ok(result) => std::task::Poll::Ready(result),
519 Err(_) => std::task::Poll::Pending,
520 },
521 std::task::Poll::Ready(Err(e)) => std::task::Poll::Ready(Err(e)),
522 std::task::Poll::Pending => std::task::Poll::Pending,
523 }
524 }
525
526 pub async fn send_from(&mut self, buf: &[u8], from: A) -> std::io::Result<TimestampData> {
532 let from = from.to_sockaddr(PrivateToken);
533
534 if let Some(token) = self
535 .socket
536 .async_io(Interest::WRITABLE, |socket| {
537 self.send_inner(|| socket.send_from(buf, from))
538 })
539 .await?
540 {
541 self.send_timestamp_for_transmit(token).await
542 } else {
543 Ok(TimestampData::empty(self.timestamp_mode))
544 }
545 }
546}
547
548impl<A: MulticastJoinable, S> Socket<A, S> {
549 pub fn join_multicast(&self, addr: A, interface: InterfaceName) -> std::io::Result<()> {
550 addr.join_multicast(self.socket.get_ref().as_raw_fd(), interface, PrivateToken)
551 }
552
553 pub fn leave_multicast(&self, addr: A, interface: InterfaceName) -> std::io::Result<()> {
554 addr.leave_multicast(self.socket.get_ref().as_raw_fd(), interface, PrivateToken)
555 }
556}
557
558pub fn open_ip(
559 addr: SocketAddr,
560 timestamping: GeneralTimestampMode,
561 #[cfg_attr(not(target_os = "linux"), expect(unused))] reuse_addr: bool,
562) -> std::io::Result<Socket<SocketAddr, Open>> {
563 let socket = match addr {
565 SocketAddr::V4(_) => RawSocket::open(libc::PF_INET, libc::SOCK_DGRAM, libc::IPPROTO_UDP),
566 SocketAddr::V6(_) => RawSocket::open(libc::PF_INET6, libc::SOCK_DGRAM, libc::IPPROTO_UDP),
567 }?;
568 match addr {
569 SocketAddr::V4(_) => socket.enable_destination_ipv4()?,
570 SocketAddr::V6(_) => socket.enable_destination_ipv6()?,
571 }
572 #[cfg(target_os = "linux")]
573 if reuse_addr {
574 socket.reuse_addr()?;
575 }
576 socket.bind(addr.to_sockaddr(PrivateToken))?;
577 socket.set_nonblocking(true)?;
578 configure_timestamping(&socket, None, timestamping.into(), None)?;
579
580 let local_addr = SocketAddr::from_sockaddr(socket.getsockname()?, PrivateToken)
581 .ok_or::<std::io::Error>(std::io::ErrorKind::Other.into())?;
582
583 Ok(Socket {
584 timestamp_mode: timestamping.into(),
585 socket: Arc::new(AsyncFd::new(socket)?),
586 #[cfg(target_os = "linux")]
587 send_counter: std::sync::Mutex::new(0),
588 local_addr,
589 _state: PhantomData,
590 })
591}
592
593pub fn open_ipv4(
594 addr: SocketAddrV4,
595 timestamping: GeneralTimestampMode,
596 #[cfg_attr(not(target_os = "linux"), expect(unused))] reuse_addr: bool,
597) -> std::io::Result<Socket<SocketAddrV4, Open>> {
598 let socket = RawSocket::open(libc::PF_INET, libc::SOCK_DGRAM, libc::IPPROTO_UDP)?;
600 socket.enable_destination_ipv4()?;
601 #[cfg(target_os = "linux")]
602 if reuse_addr {
603 socket.reuse_addr()?;
604 }
605 socket.bind(addr.to_sockaddr(PrivateToken))?;
606 socket.set_nonblocking(true)?;
607 configure_timestamping(&socket, None, timestamping.into(), None)?;
608
609 let local_addr = SocketAddrV4::from_sockaddr(socket.getsockname()?, PrivateToken)
610 .ok_or::<std::io::Error>(std::io::ErrorKind::Other.into())?;
611
612 Ok(Socket {
613 timestamp_mode: timestamping.into(),
614 socket: Arc::new(AsyncFd::new(socket)?),
615 #[cfg(target_os = "linux")]
616 send_counter: std::sync::Mutex::new(0),
617 local_addr,
618 _state: PhantomData,
619 })
620}
621
622pub fn open_ipv6(
623 addr: SocketAddrV6,
624 timestamping: GeneralTimestampMode,
625 #[cfg_attr(not(target_os = "linux"), expect(unused))] reuse_addr: bool,
626) -> std::io::Result<Socket<SocketAddrV6, Open>> {
627 let socket = RawSocket::open(libc::PF_INET6, libc::SOCK_DGRAM, libc::IPPROTO_UDP)?;
629 socket.ipv6_only()?;
630 socket.enable_destination_ipv6()?;
631 #[cfg(target_os = "linux")]
632 if reuse_addr {
633 socket.reuse_addr()?;
634 }
635 socket.bind(addr.to_sockaddr(PrivateToken))?;
636 socket.set_nonblocking(true)?;
637 configure_timestamping(&socket, None, timestamping.into(), None)?;
638
639 let local_addr = SocketAddrV6::from_sockaddr(socket.getsockname()?, PrivateToken)
640 .ok_or::<std::io::Error>(std::io::ErrorKind::Other.into())?;
641
642 Ok(Socket {
643 timestamp_mode: timestamping.into(),
644 socket: Arc::new(AsyncFd::new(socket)?),
645 #[cfg(target_os = "linux")]
646 send_counter: std::sync::Mutex::new(0),
647 local_addr,
648 _state: PhantomData,
649 })
650}
651
652pub fn connect_address(
653 addr: SocketAddr,
654 timestamping: GeneralTimestampMode,
655) -> std::io::Result<Socket<SocketAddr, Connected>> {
656 let socket = match addr {
658 SocketAddr::V4(_) => RawSocket::open(libc::PF_INET, libc::SOCK_DGRAM, libc::IPPROTO_UDP),
659 SocketAddr::V6(_) => RawSocket::open(libc::PF_INET6, libc::SOCK_DGRAM, libc::IPPROTO_UDP),
660 }?;
661 match addr {
662 SocketAddr::V4(_) => socket.enable_destination_ipv4()?,
663 SocketAddr::V6(_) => socket.enable_destination_ipv6()?,
664 }
665 socket.connect(addr.to_sockaddr(PrivateToken))?;
666 socket.set_nonblocking(true)?;
667 configure_timestamping(&socket, None, timestamping.into(), None)?;
668
669 let local_addr = SocketAddr::from_sockaddr(socket.getsockname()?, PrivateToken)
670 .ok_or::<std::io::Error>(std::io::ErrorKind::Other.into())?;
671
672 Ok(Socket {
673 timestamp_mode: timestamping.into(),
674 socket: Arc::new(AsyncFd::new(socket)?),
675 #[cfg(target_os = "linux")]
676 send_counter: std::sync::Mutex::new(0),
677 local_addr,
678 _state: PhantomData,
679 })
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
686
687 #[tokio::test]
688 async fn test_open_ip() {
689 let mut a = open_ip(
690 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5125),
691 GeneralTimestampMode::None,
692 false,
693 )
694 .unwrap();
695 let mut b = connect_address(
696 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5125),
697 GeneralTimestampMode::None,
698 )
699 .unwrap();
700 assert!(b.send(&[1, 2, 3]).await.is_ok());
701 let mut buf = [0; 4];
702 let recv_result = a.recv(&mut buf).await.unwrap();
703 assert_eq!(recv_result.bytes_read, 3);
704 assert_eq!(&buf[0..3], &[1, 2, 3]);
705 assert!(a.send_to(&[4, 5, 6], recv_result.remote_addr).await.is_ok());
706 let recv_result = b.recv(&mut buf).await.unwrap();
707 assert_eq!(recv_result.bytes_read, 3);
708 assert_eq!(&buf[0..3], &[4, 5, 6]);
709 }
710
711 #[tokio::test]
712 async fn test_open_ip_dest_addr() {
713 let a = open_ip(
714 SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 5127),
715 GeneralTimestampMode::None,
716 false,
717 )
718 .unwrap();
719 let mut b = connect_address(
720 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5127),
721 GeneralTimestampMode::None,
722 )
723 .unwrap();
724 assert!(b.send(&[1, 2, 3]).await.is_ok());
725 let mut buf = [0; 4];
726 let recv_result = a.recv(&mut buf).await.unwrap();
727 assert_eq!(recv_result.bytes_read, 3);
728 assert_eq!(&buf[0..3], &[1, 2, 3]);
729 assert_eq!(
730 recv_result.local_addr,
731 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5127)
732 );
733 assert_ne!(a.local_addr().ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
734
735 let a = open_ip(
736 SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 5129),
737 GeneralTimestampMode::None,
738 false,
739 )
740 .unwrap();
741 let mut b = connect_address(
742 SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 5129),
743 GeneralTimestampMode::None,
744 )
745 .unwrap();
746 assert!(b.send(&[1, 2, 3]).await.is_ok());
747 let mut buf = [0; 4];
748 let recv_result = a.recv(&mut buf).await.unwrap();
749 assert_eq!(recv_result.bytes_read, 3);
750 assert_eq!(&buf[0..3], &[1, 2, 3]);
751 assert_eq!(
752 recv_result.local_addr,
753 SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 5129)
754 );
755 assert_ne!(a.local_addr().ip(), IpAddr::V6(Ipv6Addr::LOCALHOST));
756 }
757
758 #[tokio::test]
759 async fn test_send_from() {
760 let mut a = open_ip(
761 SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 5130),
762 GeneralTimestampMode::None,
763 false,
764 )
765 .unwrap();
766 let mut b = connect_address(
767 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5130),
768 GeneralTimestampMode::None,
769 )
770 .unwrap();
771 b.send_from(
772 &[1, 2, 3],
773 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
774 )
775 .await
776 .unwrap();
777 let mut buf = [0; 4];
778 let recv_result = a.recv(&mut buf).await.unwrap();
779 assert_eq!(recv_result.bytes_read, 3);
780 assert_eq!(&buf[0..3], &[1, 2, 3]);
781 assert_eq!(
782 recv_result.remote_addr.ip(),
783 IpAddr::V4(Ipv4Addr::LOCALHOST)
784 );
785
786 a.send_from_to(
787 &[1, 2, 3],
788 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
789 dbg!(b.local_addr()),
790 )
791 .await
792 .unwrap();
793 let mut buf = [0; 4];
794 let recv_result = b.recv(&mut buf).await.unwrap();
795 assert_eq!(recv_result.bytes_read, 3);
796 assert_eq!(&buf[0..3], &[1, 2, 3]);
797 assert_eq!(
798 recv_result.remote_addr.ip(),
799 IpAddr::V4(Ipv4Addr::LOCALHOST)
800 );
801 }
802
803 #[tokio::test]
804 async fn test_send_from_v6() {
805 let mut a = open_ip(
806 SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 5131),
807 GeneralTimestampMode::None,
808 false,
809 )
810 .unwrap();
811 let mut b = connect_address(
812 SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 5131),
813 GeneralTimestampMode::None,
814 )
815 .unwrap();
816 b.send_from(
817 &[1, 2, 3],
818 SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0),
819 )
820 .await
821 .unwrap();
822 let mut buf = [0; 4];
823 let recv_result = a.recv(&mut buf).await.unwrap();
824 assert_eq!(recv_result.bytes_read, 3);
825 assert_eq!(&buf[0..3], &[1, 2, 3]);
826 assert_eq!(
827 recv_result.remote_addr.ip(),
828 IpAddr::V6(Ipv6Addr::LOCALHOST)
829 );
830
831 a.send_from_to(
832 &[1, 2, 3],
833 SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0),
834 dbg!(b.local_addr()),
835 )
836 .await
837 .unwrap();
838 let mut buf = [0; 4];
839 let recv_result = b.recv(&mut buf).await.unwrap();
840 assert_eq!(recv_result.bytes_read, 3);
841 assert_eq!(&buf[0..3], &[1, 2, 3]);
842 assert_eq!(
843 recv_result.remote_addr.ip(),
844 IpAddr::V6(Ipv6Addr::LOCALHOST)
845 );
846 }
847}