Skip to content

Commit 3ad8ef1

Browse files
authored
Merge pull request #4 from sfackler/unix-socket
Unix socket support
2 parents d414908 + 3cc4a1a commit 3ad8ef1

File tree

6 files changed

+140
-3
lines changed

6 files changed

+140
-3
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ before_script:
66
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
77
script:
88
- cargo test
9-
- cargo test --features reuseport
9+
- cargo test --features "reuseport unix pair"
1010
- cargo doc --no-deps --all-features
1111
after_success:
1212
- travis-cargo --only nightly doc-upload

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Utilities for handling networking sockets with a maximal amount of configuration
1212
possible intended.
1313
"""
1414

15+
[package.metadata.docs.rs]
16+
all-features = true
17+
1518
[target."cfg(windows)".dependencies]
1619
ws2_32-sys = "0.2"
1720
winapi = "0.2"
@@ -21,5 +24,10 @@ kernel32-sys = "0.2"
2124
cfg-if = "0.1"
2225
libc = "0.2.14"
2326

27+
[dev-dependencies]
28+
tempdir = "0.3"
29+
2430
[features]
2531
reuseport = []
32+
pair = []
33+
unix = []

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
#[cfg(windows)] extern crate winapi;
4545
#[cfg(windows)] extern crate ws2_32;
4646

47+
#[cfg(test)] extern crate tempdir;
48+
4749
use utils::NetInt;
4850

4951
#[cfg(unix)] use libc::{sockaddr_storage, socklen_t};

src/sockaddr.rs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use std::net::{SocketAddrV4, SocketAddrV6, SocketAddr};
1+
use std::fmt;
22
use std::mem;
3+
use std::net::{SocketAddrV4, SocketAddrV6, SocketAddr};
34
use std::ptr;
4-
use std::fmt;
55

66
#[cfg(unix)]
77
use libc::{sockaddr, sockaddr_storage, sockaddr_in, sockaddr_in6, sa_family_t, socklen_t, AF_INET,
@@ -40,6 +40,61 @@ impl SockAddr {
4040
}
4141
}
4242

43+
/// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
44+
///
45+
/// This function is only available on Unix when the `unix` feature is
46+
/// enabled.
47+
///
48+
/// # Failure
49+
///
50+
/// Returns an error if the path is longer than `SUN_LEN`.
51+
#[cfg(all(unix, feature = "unix"))]
52+
pub fn unix<P>(path: P) -> ::std::io::Result<SockAddr>
53+
where P: AsRef<::std::path::Path>
54+
{
55+
use std::cmp::Ordering;
56+
use std::io;
57+
use std::os::unix::ffi::OsStrExt;
58+
use libc::{sockaddr_un, AF_UNIX, c_char};
59+
60+
unsafe {
61+
let mut addr = mem::zeroed::<sockaddr_un>();
62+
addr.sun_family = AF_UNIX as sa_family_t;
63+
64+
let bytes = path.as_ref().as_os_str().as_bytes();
65+
66+
match (bytes.get(0), bytes.len().cmp(&addr.sun_path.len())) {
67+
// Abstract paths don't need a null terminator
68+
(Some(&0), Ordering::Greater) => {
69+
return Err(io::Error::new(io::ErrorKind::InvalidInput,
70+
"path must be no longer than SUN_LEN"));
71+
}
72+
(Some(&0), _) => {}
73+
(_, Ordering::Greater) | (_, Ordering::Equal) => {
74+
return Err(io::Error::new(io::ErrorKind::InvalidInput,
75+
"path must be shorter than SUN_LEN"));
76+
}
77+
_ => {}
78+
}
79+
80+
for (dst, src) in addr.sun_path.iter_mut().zip(bytes) {
81+
*dst = *src as c_char;
82+
}
83+
// null byte for pathname is already there since we zeroed up front
84+
85+
let base = &addr as *const _ as usize;
86+
let path = &addr.sun_path as *const _ as usize;
87+
let sun_path_offset = path - base;
88+
89+
let mut len = sun_path_offset + bytes.len();
90+
match bytes.get(0) {
91+
Some(&0) | None => {}
92+
Some(_) => len += 1,
93+
}
94+
Ok(SockAddr::from_raw_parts(&addr as *const _ as *const _, len as socklen_t))
95+
}
96+
}
97+
4398
unsafe fn as_<T>(&self, family: sa_family_t) -> Option<T> {
4499
if self.storage.ss_family != family {
45100
return None;

src/socket.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ impl Socket {
3636
})
3737
}
3838

39+
/// Creates a pair of sockets which are connected to each other.
40+
///
41+
/// This function corresponds to `socketpair(2)`.
42+
///
43+
/// This function is only available on Unix when the `pair` feature is
44+
/// enabled.
45+
#[cfg(all(unix, feature = "pair"))]
46+
pub fn pair(domain: Domain,
47+
type_: Type,
48+
protocol: Option<Protocol>) -> io::Result<(Socket, Socket)> {
49+
let protocol = protocol.map(|p| p.0).unwrap_or(0);
50+
let sockets = sys::Socket::pair(domain.0, type_.0, protocol)?;
51+
Ok((Socket { inner: sockets.0 }, Socket { inner: sockets.1 }))
52+
}
53+
3954
/// Consumes this `Socket`, converting it to a `TcpStream`.
4055
pub fn into_tcp_stream(self) -> net::TcpStream {
4156
self.into()
@@ -627,6 +642,15 @@ impl Domain {
627642
pub fn ipv6() -> Domain {
628643
Domain(c::AF_INET6)
629644
}
645+
646+
/// Domain for Unix socket communication, corresponding to `AF_UNIX`.
647+
///
648+
/// This function is only available on Unix when the `unix` feature is
649+
/// activated.
650+
#[cfg(all(unix, feature = "unix"))]
651+
pub fn unix() -> Domain {
652+
Domain(c::AF_UNIX)
653+
}
630654
}
631655

632656
impl From<i32> for Domain {
@@ -721,4 +745,37 @@ mod test {
721745
let socket = Socket::new(Domain::ipv4(), Type::stream(), None).unwrap();
722746
socket.connect_timeout(&addr, Duration::from_millis(250)).unwrap();
723747
}
748+
749+
#[test]
750+
#[cfg(all(unix, feature = "pair", feature = "unix"))]
751+
fn pair() {
752+
let (mut a, mut b) = Socket::pair(Domain::unix(), Type::stream(), None).unwrap();
753+
a.write_all(b"hello world").unwrap();
754+
let mut buf = [0; 11];
755+
b.read_exact(&mut buf).unwrap();
756+
assert_eq!(buf, &b"hello world"[..]);
757+
}
758+
759+
#[test]
760+
#[cfg(all(unix, feature = "unix"))]
761+
fn unix() {
762+
use tempdir::TempDir;
763+
764+
let dir = TempDir::new("unix").unwrap();
765+
let addr = SockAddr::unix(dir.path().join("sock")).unwrap();
766+
767+
let listener = Socket::new(Domain::unix(), Type::stream(), None).unwrap();
768+
listener.bind(&addr).unwrap();
769+
listener.listen(10).unwrap();
770+
771+
let mut a = Socket::new(Domain::unix(), Type::stream(), None).unwrap();
772+
a.connect(&addr).unwrap();
773+
774+
let mut b = listener.accept().unwrap().0;
775+
776+
a.write_all(b"hello world").unwrap();
777+
let mut buf = [0; 11];
778+
b.read_exact(&mut buf).unwrap();
779+
assert_eq!(buf, &b"hello world"[..]);
780+
}
724781
}

src/sys/unix/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,21 @@ impl Socket {
9494
}
9595
}
9696

97+
pub fn pair(family: c_int, ty: c_int, protocol: c_int) -> io::Result<(Socket, Socket)> {
98+
unsafe {
99+
let mut fds = [0, 0];
100+
cvt(libc::socketpair(family, ty, protocol, fds.as_mut_ptr()))?;
101+
let fds = (Socket::from_raw_fd(fds[0]), Socket::from_raw_fd(fds[1]));
102+
set_cloexec(fds.0.as_raw_fd())?;
103+
set_cloexec(fds.1.as_raw_fd())?;
104+
#[cfg(target_os = "macos")] {
105+
fds.0.setsockopt(libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1i32)?;
106+
fds.1.setsockopt(libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1i32)?;
107+
}
108+
Ok(fds)
109+
}
110+
}
111+
97112
pub fn bind(&self, addr: &SockAddr) -> io::Result<()> {
98113
unsafe {
99114
cvt(libc::bind(self.fd, addr.as_ptr(), addr.len() as _)).map(|_| ())

0 commit comments

Comments
 (0)