mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-10-01 12:20:39 +00:00
net: add recv_buf
for UdpSocket
and UnixDatagram
(#5583)
This commit is contained in:
parent
663e56e983
commit
b31f1a4662
@ -825,7 +825,7 @@ impl UdpSocket {
|
|||||||
/// address to which it is connected. On success, returns the number of
|
/// address to which it is connected. On success, returns the number of
|
||||||
/// bytes read.
|
/// bytes read.
|
||||||
///
|
///
|
||||||
/// The function must be called with valid byte array buf of sufficient size
|
/// This method must be called with valid byte array buf of sufficient size
|
||||||
/// to hold the message bytes. If a message is too long to fit in the
|
/// to hold the message bytes. If a message is too long to fit in the
|
||||||
/// supplied buffer, excess bytes may be discarded.
|
/// supplied buffer, excess bytes may be discarded.
|
||||||
///
|
///
|
||||||
@ -881,10 +881,12 @@ impl UdpSocket {
|
|||||||
/// Tries to receive data from the stream into the provided buffer, advancing the
|
/// Tries to receive data from the stream into the provided buffer, advancing the
|
||||||
/// buffer's internal cursor, returning how many bytes were read.
|
/// buffer's internal cursor, returning how many bytes were read.
|
||||||
///
|
///
|
||||||
/// The function must be called with valid byte array buf of sufficient size
|
/// This method must be called with valid byte array buf of sufficient size
|
||||||
/// to hold the message bytes. If a message is too long to fit in the
|
/// to hold the message bytes. If a message is too long to fit in the
|
||||||
/// supplied buffer, excess bytes may be discarded.
|
/// supplied buffer, excess bytes may be discarded.
|
||||||
///
|
///
|
||||||
|
/// This method can be used even if `buf` is uninitialized.
|
||||||
|
///
|
||||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||||
/// returned. This function is usually paired with `readable()`.
|
/// returned. This function is usually paired with `readable()`.
|
||||||
///
|
///
|
||||||
@ -931,10 +933,10 @@ impl UdpSocket {
|
|||||||
let dst =
|
let dst =
|
||||||
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||||
|
|
||||||
// Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the
|
|
||||||
// buffer.
|
|
||||||
let n = (*self.io).recv(dst)?;
|
let n = (*self.io).recv(dst)?;
|
||||||
|
|
||||||
|
// Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the
|
||||||
|
// buffer.
|
||||||
unsafe {
|
unsafe {
|
||||||
buf.advance_mut(n);
|
buf.advance_mut(n);
|
||||||
}
|
}
|
||||||
@ -943,13 +945,63 @@ impl UdpSocket {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Receives a single datagram message on the socket from the remote address
|
||||||
|
/// to which it is connected, advancing the buffer's internal cursor,
|
||||||
|
/// returning how many bytes were read.
|
||||||
|
///
|
||||||
|
/// This method must be called with valid byte array buf of sufficient size
|
||||||
|
/// to hold the message bytes. If a message is too long to fit in the
|
||||||
|
/// supplied buffer, excess bytes may be discarded.
|
||||||
|
///
|
||||||
|
/// This method can be used even if `buf` is uninitialized.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use tokio::net::UdpSocket;
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// #[tokio::main]
|
||||||
|
/// async fn main() -> io::Result<()> {
|
||||||
|
/// // Connect to a peer
|
||||||
|
/// let socket = UdpSocket::bind("127.0.0.1:8080").await?;
|
||||||
|
/// socket.connect("127.0.0.1:8081").await?;
|
||||||
|
///
|
||||||
|
/// let mut buf = Vec::with_capacity(512);
|
||||||
|
/// let len = socket.recv_buf(&mut buf).await?;
|
||||||
|
///
|
||||||
|
/// println!("received {} bytes {:?}", len, &buf[..len]);
|
||||||
|
///
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub async fn recv_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||||
|
self.io.registration().async_io(Interest::READABLE, || {
|
||||||
|
let dst = buf.chunk_mut();
|
||||||
|
let dst =
|
||||||
|
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||||
|
|
||||||
|
let n = (*self.io).recv(dst)?;
|
||||||
|
|
||||||
|
// Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the
|
||||||
|
// buffer.
|
||||||
|
unsafe {
|
||||||
|
buf.advance_mut(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(n)
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to receive a single datagram message on the socket. On success,
|
/// Tries to receive a single datagram message on the socket. On success,
|
||||||
/// returns the number of bytes read and the origin.
|
/// returns the number of bytes read and the origin.
|
||||||
///
|
///
|
||||||
/// The function must be called with valid byte array buf of sufficient size
|
/// This method must be called with valid byte array buf of sufficient size
|
||||||
/// to hold the message bytes. If a message is too long to fit in the
|
/// to hold the message bytes. If a message is too long to fit in the
|
||||||
/// supplied buffer, excess bytes may be discarded.
|
/// supplied buffer, excess bytes may be discarded.
|
||||||
///
|
///
|
||||||
|
/// This method can be used even if `buf` is uninitialized.
|
||||||
|
///
|
||||||
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
|
||||||
/// returned. This function is usually paired with `readable()`.
|
/// returned. This function is usually paired with `readable()`.
|
||||||
///
|
///
|
||||||
@ -1004,10 +1056,10 @@ impl UdpSocket {
|
|||||||
let dst =
|
let dst =
|
||||||
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||||
|
|
||||||
// Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the
|
|
||||||
// buffer.
|
|
||||||
let (n, addr) = (*self.io).recv_from(dst)?;
|
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||||
|
|
||||||
|
// Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the
|
||||||
|
// buffer.
|
||||||
unsafe {
|
unsafe {
|
||||||
buf.advance_mut(n);
|
buf.advance_mut(n);
|
||||||
}
|
}
|
||||||
@ -1015,6 +1067,62 @@ impl UdpSocket {
|
|||||||
Ok((n, addr))
|
Ok((n, addr))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Receives a single datagram message on the socket, advancing the
|
||||||
|
/// buffer's internal cursor, returning how many bytes were read and the origin.
|
||||||
|
///
|
||||||
|
/// This method must be called with valid byte array buf of sufficient size
|
||||||
|
/// to hold the message bytes. If a message is too long to fit in the
|
||||||
|
/// supplied buffer, excess bytes may be discarded.
|
||||||
|
///
|
||||||
|
/// This method can be used even if `buf` is uninitialized.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
|
||||||
|
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
|
||||||
|
/// Because UDP is stateless and does not validate the origin of a packet,
|
||||||
|
/// the attacker does not need to be able to intercept traffic in order to interfere.
|
||||||
|
/// It is important to be aware of this when designing your application-level protocol.
|
||||||
|
///
|
||||||
|
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use tokio::net::UdpSocket;
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// #[tokio::main]
|
||||||
|
/// async fn main() -> io::Result<()> {
|
||||||
|
/// // Connect to a peer
|
||||||
|
/// let socket = UdpSocket::bind("127.0.0.1:8080").await?;
|
||||||
|
/// socket.connect("127.0.0.1:8081").await?;
|
||||||
|
///
|
||||||
|
/// let mut buf = Vec::with_capacity(512);
|
||||||
|
/// let (len, addr) = socket.recv_buf_from(&mut buf).await?;
|
||||||
|
///
|
||||||
|
/// println!("received {:?} bytes from {:?}", len, addr);
|
||||||
|
///
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub async fn recv_buf_from<B: BufMut>(&self, buf: &mut B) -> io::Result<(usize, SocketAddr)> {
|
||||||
|
self.io.registration().async_io(Interest::READABLE, || {
|
||||||
|
let dst = buf.chunk_mut();
|
||||||
|
let dst =
|
||||||
|
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||||
|
|
||||||
|
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||||
|
|
||||||
|
// Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the
|
||||||
|
// buffer.
|
||||||
|
unsafe {
|
||||||
|
buf.advance_mut(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((n,addr))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends data on the socket to the given address. On success, returns the
|
/// Sends data on the socket to the given address. On success, returns the
|
||||||
@ -1252,7 +1360,7 @@ impl UdpSocket {
|
|||||||
/// Tries to receive a single datagram message on the socket. On success,
|
/// Tries to receive a single datagram message on the socket. On success,
|
||||||
/// returns the number of bytes read and the origin.
|
/// returns the number of bytes read and the origin.
|
||||||
///
|
///
|
||||||
/// The function must be called with valid byte array buf of sufficient size
|
/// This method must be called with valid byte array buf of sufficient size
|
||||||
/// to hold the message bytes. If a message is too long to fit in the
|
/// to hold the message bytes. If a message is too long to fit in the
|
||||||
/// supplied buffer, excess bytes may be discarded.
|
/// supplied buffer, excess bytes may be discarded.
|
||||||
///
|
///
|
||||||
|
@ -807,6 +807,8 @@ impl UnixDatagram {
|
|||||||
cfg_io_util! {
|
cfg_io_util! {
|
||||||
/// Tries to receive data from the socket without waiting.
|
/// Tries to receive data from the socket without waiting.
|
||||||
///
|
///
|
||||||
|
/// This method can be used even if `buf` is uninitialized.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
@ -866,9 +868,64 @@ impl UnixDatagram {
|
|||||||
Ok((n, SocketAddr(addr)))
|
Ok((n, SocketAddr(addr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Receives from the socket, advances the
|
||||||
|
/// buffer's internal cursor and returns how many bytes were read and the origin.
|
||||||
|
///
|
||||||
|
/// This method can be used even if `buf` is uninitialized.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use std::error::Error;
|
||||||
|
/// # #[tokio::main]
|
||||||
|
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
/// use tokio::net::UnixDatagram;
|
||||||
|
/// use tempfile::tempdir;
|
||||||
|
///
|
||||||
|
/// // We use a temporary directory so that the socket
|
||||||
|
/// // files left by the bound sockets will get cleaned up.
|
||||||
|
/// let tmp = tempdir()?;
|
||||||
|
///
|
||||||
|
/// // Bind each socket to a filesystem path
|
||||||
|
/// let tx_path = tmp.path().join("tx");
|
||||||
|
/// let tx = UnixDatagram::bind(&tx_path)?;
|
||||||
|
/// let rx_path = tmp.path().join("rx");
|
||||||
|
/// let rx = UnixDatagram::bind(&rx_path)?;
|
||||||
|
///
|
||||||
|
/// let bytes = b"hello world";
|
||||||
|
/// tx.send_to(bytes, &rx_path).await?;
|
||||||
|
///
|
||||||
|
/// let mut buf = Vec::with_capacity(24);
|
||||||
|
/// let (size, addr) = rx.recv_buf_from(&mut buf).await?;
|
||||||
|
///
|
||||||
|
/// let dgram = &buf[..size];
|
||||||
|
/// assert_eq!(dgram, bytes);
|
||||||
|
/// assert_eq!(addr.as_pathname().unwrap(), &tx_path);
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub async fn recv_buf_from<B: BufMut>(&self, buf: &mut B) -> io::Result<(usize, SocketAddr)> {
|
||||||
|
self.io.registration().async_io(Interest::READABLE, || {
|
||||||
|
let dst = buf.chunk_mut();
|
||||||
|
let dst =
|
||||||
|
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||||
|
|
||||||
|
// Safety: We trust `UnixDatagram::recv_from` to have filled up `n` bytes in the
|
||||||
|
// buffer.
|
||||||
|
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
buf.advance_mut(n);
|
||||||
|
}
|
||||||
|
Ok((n,SocketAddr(addr)))
|
||||||
|
}).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||||
/// buffer's internal cursor, returning how many bytes were read.
|
/// buffer's internal cursor, returning how many bytes were read.
|
||||||
///
|
///
|
||||||
|
/// This method can be used even if `buf` is uninitialized.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
@ -926,6 +983,52 @@ impl UnixDatagram {
|
|||||||
Ok(n)
|
Ok(n)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Receives data from the socket from the address to which it is connected,
|
||||||
|
/// advancing the buffer's internal cursor, returning how many bytes were read.
|
||||||
|
///
|
||||||
|
/// This method can be used even if `buf` is uninitialized.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use std::error::Error;
|
||||||
|
/// # #[tokio::main]
|
||||||
|
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
/// use tokio::net::UnixDatagram;
|
||||||
|
///
|
||||||
|
/// // Create the pair of sockets
|
||||||
|
/// let (sock1, sock2) = UnixDatagram::pair()?;
|
||||||
|
///
|
||||||
|
/// // Since the sockets are paired, the paired send/recv
|
||||||
|
/// // functions can be used
|
||||||
|
/// let bytes = b"hello world";
|
||||||
|
/// sock1.send(bytes).await?;
|
||||||
|
///
|
||||||
|
/// let mut buff = Vec::with_capacity(24);
|
||||||
|
/// let size = sock2.recv_buf(&mut buff).await?;
|
||||||
|
///
|
||||||
|
/// let dgram = &buff[..size];
|
||||||
|
/// assert_eq!(dgram, bytes);
|
||||||
|
///
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub async fn recv_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||||
|
self.io.registration().async_io(Interest::READABLE, || {
|
||||||
|
let dst = buf.chunk_mut();
|
||||||
|
let dst =
|
||||||
|
unsafe { &mut *(dst as *mut _ as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]) };
|
||||||
|
|
||||||
|
// Safety: We trust `UnixDatagram::recv_from` to have filled up `n` bytes in the
|
||||||
|
// buffer.
|
||||||
|
let n = (*self.io).recv(dst)?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
buf.advance_mut(n);
|
||||||
|
}
|
||||||
|
Ok(n)
|
||||||
|
}).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends data on the socket to the specified address.
|
/// Sends data on the socket to the specified address.
|
||||||
|
@ -524,6 +524,23 @@ async fn try_recv_buf() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn recv_buf() -> std::io::Result<()> {
|
||||||
|
let sender = UdpSocket::bind("127.0.0.1:0").await?;
|
||||||
|
let receiver = UdpSocket::bind("127.0.0.1:0").await?;
|
||||||
|
|
||||||
|
sender.connect(receiver.local_addr()?).await?;
|
||||||
|
receiver.connect(sender.local_addr()?).await?;
|
||||||
|
|
||||||
|
sender.send(MSG).await?;
|
||||||
|
let mut recv_buf = Vec::with_capacity(32);
|
||||||
|
let len = receiver.recv_buf(&mut recv_buf).await?;
|
||||||
|
|
||||||
|
assert_eq!(len, MSG_LEN);
|
||||||
|
assert_eq!(&recv_buf[..len], MSG);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn try_recv_buf_from() {
|
async fn try_recv_buf_from() {
|
||||||
// Create listener
|
// Create listener
|
||||||
@ -567,6 +584,23 @@ async fn try_recv_buf_from() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn recv_buf_from() -> std::io::Result<()> {
|
||||||
|
let sender = UdpSocket::bind("127.0.0.1:0").await?;
|
||||||
|
let receiver = UdpSocket::bind("127.0.0.1:0").await?;
|
||||||
|
|
||||||
|
sender.connect(receiver.local_addr()?).await?;
|
||||||
|
|
||||||
|
sender.send(MSG).await?;
|
||||||
|
let mut recv_buf = Vec::with_capacity(32);
|
||||||
|
let (len, caddr) = receiver.recv_buf_from(&mut recv_buf).await?;
|
||||||
|
|
||||||
|
assert_eq!(len, MSG_LEN);
|
||||||
|
assert_eq!(&recv_buf[..len], MSG);
|
||||||
|
assert_eq!(caddr, sender.local_addr()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn poll_ready() {
|
async fn poll_ready() {
|
||||||
// Create listener
|
// Create listener
|
||||||
|
@ -277,6 +277,28 @@ async fn try_recv_buf_from() -> std::io::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn recv_buf_from() -> std::io::Result<()> {
|
||||||
|
let tmp = tempfile::tempdir()?;
|
||||||
|
|
||||||
|
// Bind each socket to a filesystem path
|
||||||
|
let tx_path = tmp.path().join("tx");
|
||||||
|
let tx = UnixDatagram::bind(&tx_path)?;
|
||||||
|
let rx_path = tmp.path().join("rx");
|
||||||
|
let rx = UnixDatagram::bind(&rx_path)?;
|
||||||
|
|
||||||
|
let bytes = b"hello world";
|
||||||
|
tx.send_to(bytes, &rx_path).await?;
|
||||||
|
|
||||||
|
let mut buf = Vec::with_capacity(24);
|
||||||
|
let (size, addr) = rx.recv_buf_from(&mut buf).await?;
|
||||||
|
|
||||||
|
let dgram = &buf[..size];
|
||||||
|
assert_eq!(dgram, bytes);
|
||||||
|
assert_eq!(addr.as_pathname().unwrap(), &tx_path);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Even though we use sync non-blocking io we still need a reactor.
|
// Even though we use sync non-blocking io we still need a reactor.
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn try_recv_buf_never_block() -> io::Result<()> {
|
async fn try_recv_buf_never_block() -> io::Result<()> {
|
||||||
@ -325,6 +347,24 @@ async fn try_recv_buf_never_block() -> io::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn recv_buf() -> std::io::Result<()> {
|
||||||
|
// Create the pair of sockets
|
||||||
|
let (sock1, sock2) = UnixDatagram::pair()?;
|
||||||
|
|
||||||
|
// Since the sockets are paired, the paired send/recv
|
||||||
|
// functions can be used
|
||||||
|
let bytes = b"hello world";
|
||||||
|
sock1.send(bytes).await?;
|
||||||
|
|
||||||
|
let mut buff = Vec::with_capacity(24);
|
||||||
|
let size = sock2.recv_buf(&mut buff).await?;
|
||||||
|
|
||||||
|
let dgram = &buff[..size];
|
||||||
|
assert_eq!(dgram, bytes);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn poll_ready() -> io::Result<()> {
|
async fn poll_ready() -> io::Result<()> {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user