Skip to content

Commit d459a93

Browse files
authored
net: add UdpSocket::peek_sender() (#5520)
1 parent f177aad commit d459a93

File tree

3 files changed

+304
-3
lines changed

3 files changed

+304
-3
lines changed

tokio/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ num_cpus = { version = "1.8.0", optional = true }
109109
parking_lot = { version = "0.12.0", optional = true }
110110

111111
[target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies]
112-
socket2 = { version = "0.4.4", optional = true, features = [ "all" ] }
112+
socket2 = { version = "0.4.9", optional = true, features = [ "all" ] }
113113

114114
# Currently unstable. The API exposed by these features may be broken at any time.
115115
# Requires `--cfg tokio_unstable` to enable.
@@ -146,7 +146,7 @@ mockall = "0.11.1"
146146
async-stream = "0.3"
147147

148148
[target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dev-dependencies]
149-
socket2 = "0.4"
149+
socket2 = "0.4.9"
150150
tempfile = "3.1.0"
151151

152152
[target.'cfg(not(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown")))'.dev-dependencies]

tokio/src/net/udp.rs

Lines changed: 177 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,15 @@ impl UdpSocket {
954954
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
955955
/// returned. This function is usually paired with `readable()`.
956956
///
957+
/// # Notes
958+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
959+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
960+
/// Because UDP is stateless and does not validate the origin of a packet,
961+
/// the attacker does not need to be able to intercept traffic in order to interfere.
962+
/// It is important to be aware of this when designing your application-level protocol.
963+
///
964+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
965+
///
957966
/// # Examples
958967
///
959968
/// ```no_run
@@ -1177,6 +1186,15 @@ impl UdpSocket {
11771186
/// Ok(())
11781187
/// }
11791188
/// ```
1189+
///
1190+
/// # Notes
1191+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1192+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1193+
/// Because UDP is stateless and does not validate the origin of a packet,
1194+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1195+
/// It is important to be aware of this when designing your application-level protocol.
1196+
///
1197+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
11801198
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
11811199
self.io
11821200
.registration()
@@ -1201,6 +1219,15 @@ impl UdpSocket {
12011219
/// # Errors
12021220
///
12031221
/// This function may encounter any standard I/O error except `WouldBlock`.
1222+
///
1223+
/// # Notes
1224+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1225+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1226+
/// Because UDP is stateless and does not validate the origin of a packet,
1227+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1228+
/// It is important to be aware of this when designing your application-level protocol.
1229+
///
1230+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
12041231
pub fn poll_recv_from(
12051232
&self,
12061233
cx: &mut Context<'_>,
@@ -1233,6 +1260,16 @@ impl UdpSocket {
12331260
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
12341261
/// returned. This function is usually paired with `readable()`.
12351262
///
1263+
/// # Notes
1264+
///
1265+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1266+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1267+
/// Because UDP is stateless and does not validate the origin of a packet,
1268+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1269+
/// It is important to be aware of this when designing your application-level protocol.
1270+
///
1271+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
1272+
///
12361273
/// # Examples
12371274
///
12381275
/// ```no_run
@@ -1367,6 +1404,17 @@ impl UdpSocket {
13671404
/// Make sure to always use a sufficiently large buffer to hold the
13681405
/// maximum UDP packet size, which can be up to 65536 bytes in size.
13691406
///
1407+
/// MacOS will return an error if you pass a zero-sized buffer.
1408+
///
1409+
/// If you're merely interested in learning the sender of the data at the head of the queue,
1410+
/// try [`peek_sender`].
1411+
///
1412+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1413+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1414+
/// Because UDP is stateless and does not validate the origin of a packet,
1415+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1416+
/// It is important to be aware of this when designing your application-level protocol.
1417+
///
13701418
/// # Examples
13711419
///
13721420
/// ```no_run
@@ -1385,6 +1433,9 @@ impl UdpSocket {
13851433
/// Ok(())
13861434
/// }
13871435
/// ```
1436+
///
1437+
/// [`peek_sender`]: method@Self::peek_sender
1438+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
13881439
pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
13891440
self.io
13901441
.registration()
@@ -1393,7 +1444,7 @@ impl UdpSocket {
13931444
}
13941445

13951446
/// Receives data from the socket, without removing it from the input queue.
1396-
/// On success, returns the number of bytes read.
1447+
/// On success, returns the sending address of the datagram.
13971448
///
13981449
/// # Notes
13991450
///
@@ -1407,6 +1458,17 @@ impl UdpSocket {
14071458
/// Make sure to always use a sufficiently large buffer to hold the
14081459
/// maximum UDP packet size, which can be up to 65536 bytes in size.
14091460
///
1461+
/// MacOS will return an error if you pass a zero-sized buffer.
1462+
///
1463+
/// If you're merely interested in learning the sender of the data at the head of the queue,
1464+
/// try [`poll_peek_sender`].
1465+
///
1466+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1467+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1468+
/// Because UDP is stateless and does not validate the origin of a packet,
1469+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1470+
/// It is important to be aware of this when designing your application-level protocol.
1471+
///
14101472
/// # Return value
14111473
///
14121474
/// The function returns:
@@ -1418,6 +1480,9 @@ impl UdpSocket {
14181480
/// # Errors
14191481
///
14201482
/// This function may encounter any standard I/O error except `WouldBlock`.
1483+
///
1484+
/// [`poll_peek_sender`]: method@Self::poll_peek_sender
1485+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
14211486
pub fn poll_peek_from(
14221487
&self,
14231488
cx: &mut Context<'_>,
@@ -1440,6 +1505,117 @@ impl UdpSocket {
14401505
Poll::Ready(Ok(addr))
14411506
}
14421507

1508+
/// Tries to receive data on the socket without removing it from the input queue.
1509+
/// On success, returns the number of bytes read and the sending address of the
1510+
/// datagram.
1511+
///
1512+
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
1513+
/// returned. This function is usually paired with `readable()`.
1514+
///
1515+
/// # Notes
1516+
///
1517+
/// On Windows, if the data is larger than the buffer specified, the buffer
1518+
/// is filled with the first part of the data, and peek returns the error
1519+
/// WSAEMSGSIZE(10040). The excess data is lost.
1520+
/// Make sure to always use a sufficiently large buffer to hold the
1521+
/// maximum UDP packet size, which can be up to 65536 bytes in size.
1522+
///
1523+
/// MacOS will return an error if you pass a zero-sized buffer.
1524+
///
1525+
/// If you're merely interested in learning the sender of the data at the head of the queue,
1526+
/// try [`try_peek_sender`].
1527+
///
1528+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1529+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1530+
/// Because UDP is stateless and does not validate the origin of a packet,
1531+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1532+
/// It is important to be aware of this when designing your application-level protocol.
1533+
///
1534+
/// [`try_peek_sender`]: method@Self::try_peek_sender
1535+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
1536+
pub fn try_peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
1537+
self.io
1538+
.registration()
1539+
.try_io(Interest::READABLE, || self.io.peek_from(buf))
1540+
}
1541+
1542+
/// Retrieve the sender of the data at the head of the input queue, waiting if empty.
1543+
///
1544+
/// This is equivalent to calling [`peek_from`] with a zero-sized buffer,
1545+
/// but suppresses the `WSAEMSGSIZE` error on Windows and the "invalid argument" error on macOS.
1546+
///
1547+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1548+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1549+
/// Because UDP is stateless and does not validate the origin of a packet,
1550+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1551+
/// It is important to be aware of this when designing your application-level protocol.
1552+
///
1553+
/// [`peek_from`]: method@Self::peek_from
1554+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
1555+
pub async fn peek_sender(&self) -> io::Result<SocketAddr> {
1556+
self.io
1557+
.registration()
1558+
.async_io(Interest::READABLE, || self.peek_sender_inner())
1559+
.await
1560+
}
1561+
1562+
/// Retrieve the sender of the data at the head of the input queue,
1563+
/// scheduling a wakeup if empty.
1564+
///
1565+
/// This is equivalent to calling [`poll_peek_from`] with a zero-sized buffer,
1566+
/// but suppresses the `WSAEMSGSIZE` error on Windows and the "invalid argument" error on macOS.
1567+
///
1568+
/// # Notes
1569+
///
1570+
/// Note that on multiple calls to a `poll_*` method in the recv direction, only the
1571+
/// `Waker` from the `Context` passed to the most recent call will be scheduled to
1572+
/// receive a wakeup.
1573+
///
1574+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1575+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1576+
/// Because UDP is stateless and does not validate the origin of a packet,
1577+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1578+
/// It is important to be aware of this when designing your application-level protocol.
1579+
///
1580+
/// [`poll_peek_from`]: method@Self::poll_peek_from
1581+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
1582+
pub fn poll_peek_sender(&self, cx: &mut Context<'_>) -> Poll<io::Result<SocketAddr>> {
1583+
self.io
1584+
.registration()
1585+
.poll_read_io(cx, || self.peek_sender_inner())
1586+
}
1587+
1588+
/// Try to retrieve the sender of the data at the head of the input queue.
1589+
///
1590+
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is
1591+
/// returned. This function is usually paired with `readable()`.
1592+
///
1593+
/// Note that the socket address **cannot** be implicitly trusted, because it is relatively
1594+
/// trivial to send a UDP datagram with a spoofed origin in a [packet injection attack].
1595+
/// Because UDP is stateless and does not validate the origin of a packet,
1596+
/// the attacker does not need to be able to intercept traffic in order to interfere.
1597+
/// It is important to be aware of this when designing your application-level protocol.
1598+
///
1599+
/// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
1600+
pub fn try_peek_sender(&self) -> io::Result<SocketAddr> {
1601+
self.io
1602+
.registration()
1603+
.try_io(Interest::READABLE, || self.peek_sender_inner())
1604+
}
1605+
1606+
#[inline]
1607+
fn peek_sender_inner(&self) -> io::Result<SocketAddr> {
1608+
self.io.try_io(|| {
1609+
self.as_socket()
1610+
.peek_sender()?
1611+
// May be `None` if the platform doesn't populate the sender for some reason.
1612+
// In testing, that only occurred on macOS if you pass a zero-sized buffer,
1613+
// but the implementation of `Socket::peek_sender()` covers that.
1614+
.as_socket()
1615+
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "sender not available"))
1616+
})
1617+
}
1618+
14431619
/// Gets the value of the `SO_BROADCAST` option for this socket.
14441620
///
14451621
/// For more information about this option, see [`set_broadcast`].

0 commit comments

Comments
 (0)