summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Somers <asomers@gmail.com>2019-10-27 13:33:40 -0600
committerAlan Somers <asomers@gmail.com>2019-10-28 20:57:52 -0600
commite10e5373e887c96a1549e22e3dd05f448c040d1d (patch)
treecdc1b823a44dfef76fb5f7e18358e9cfad2c2368
parenta2bbbae3812ec5c1b591782394bcef478416b013 (diff)
downloadnix-e10e5373e887c96a1549e22e3dd05f448c040d1d.zip
Fix sys::socket::recvfrom for TCP sockets
recvfrom(2) only returns the sender's address for protocols that provide it. Usually, that means it returns the sender's address for datagram sockets but not for stream sockets. Fixes #1144
-rw-r--r--CHANGELOG.md4
-rw-r--r--src/sys/socket/mod.rs16
-rw-r--r--test/sys/test_socket.rs67
3 files changed, 82 insertions, 5 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa18a429..62953531 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#1133](https://github.com/nix-rust/nix/pull/1133))
### Changed
+- `sys::socket::recvfrom` now returns
+ `Result<(usize, Option<SockAddr>)>` instead of `Result<(usize, SockAddr)>`.
+ ([#1145](https://github.com/nix-rust/nix/pull/1145))
+
- `Signal::from_c_int` has been replaced by `Signal::try_from`
([#1113](https://github.com/nix-rust/nix/pull/1113))
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index 9e8cefae..3ee02c3a 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -1075,10 +1075,13 @@ pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> {
}
/// Receive data from a connectionless or connection-oriented socket. Returns
-/// the number of bytes read and the socket address of the sender.
+/// the number of bytes read and, for connectionless sockets, the socket
+/// address of the sender.
///
/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html)
-pub fn recvfrom(sockfd: RawFd, buf: &mut [u8]) -> Result<(usize, SockAddr)> {
+pub fn recvfrom(sockfd: RawFd, buf: &mut [u8])
+ -> Result<(usize, Option<SockAddr>)>
+{
unsafe {
let mut addr: sockaddr_storage = mem::zeroed();
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
@@ -1089,10 +1092,13 @@ pub fn recvfrom(sockfd: RawFd, buf: &mut [u8]) -> Result<(usize, SockAddr)> {
buf.len() as size_t,
0,
&mut addr as *mut libc::sockaddr_storage as *mut libc::sockaddr,
- &mut len as *mut socklen_t))?;
+ &mut len as *mut socklen_t))? as usize;
- sockaddr_storage_to_addr(&addr, len as usize)
- .map(|addr| (ret as usize, addr))
+ match sockaddr_storage_to_addr(&addr, len as usize) {
+ Err(Error::Sys(Errno::ENOTCONN)) => Ok((ret, None)),
+ Ok(addr) => Ok((ret, Some(addr))),
+ Err(e) => Err(e)
+ }
}
}
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index 106428b9..13d7b6b0 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -161,6 +161,73 @@ pub fn test_socketpair() {
assert_eq!(&buf[..], b"hello");
}
+mod recvfrom {
+ use nix::Result;
+ use nix::sys::socket::*;
+ use std::thread;
+ use super::*;
+
+ const MSG: &'static [u8] = b"Hello, World!";
+
+ fn sendrecv<F>(rsock: RawFd, ssock: RawFd, f: F) -> Option<SockAddr>
+ where F: Fn(RawFd, &[u8], MsgFlags) -> Result<usize> + Send + 'static
+ {
+ let mut buf: [u8; 13] = [0u8; 13];
+ let mut l = 0;
+ let mut from = None;
+
+ let send_thread = thread::spawn(move || {
+ let mut l = 0;
+ while l < std::mem::size_of_val(MSG) {
+ l += f(ssock, &MSG[l..], MsgFlags::empty()).unwrap();
+ }
+ });
+
+ while l < std::mem::size_of_val(MSG) {
+ let (len, from_) = recvfrom(rsock, &mut buf[l..]).unwrap();
+ from = from_;
+ l += len;
+ }
+ assert_eq!(&buf, MSG);
+ send_thread.join().unwrap();
+ from
+ }
+
+ #[test]
+ pub fn stream() {
+ let (fd2, fd1) = socketpair(AddressFamily::Unix, SockType::Stream,
+ None, SockFlag::empty()).unwrap();
+ // Ignore from for stream sockets
+ let _ = sendrecv(fd1, fd2, |s, m, flags| {
+ send(s, m, flags)
+ });
+ }
+
+ #[test]
+ pub fn udp() {
+ let std_sa = SocketAddr::from_str("127.0.0.1:6789").unwrap();
+ let inet_addr = InetAddr::from_std(&std_sa);
+ let sock_addr = SockAddr::new_inet(inet_addr);
+ let rsock = socket(AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None
+ ).unwrap();
+ bind(rsock, &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("send socket failed");
+ let from = sendrecv(rsock, ssock, move |s, m, flags| {
+ sendto(s, m, &sock_addr, flags)
+ });
+ // UDP sockets should set the from address
+ assert_eq!(AddressFamily::Inet, from.unwrap().family());
+ }
+}
+
// Test error handling of our recvmsg wrapper
#[test]
pub fn test_recvmsg_ebadf() {