-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Implement Unix domain sockets in libnative #11484
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ use std::libc; | |
use std::mem; | ||
use std::rt::rtio; | ||
use std::unstable::intrinsics; | ||
use std::c_str::CString; | ||
|
||
use super::{IoResult, retry}; | ||
use super::file::keep_going; | ||
|
@@ -88,6 +89,22 @@ fn addr_to_sockaddr(addr: ip::SocketAddr) -> (libc::sockaddr_storage, uint) { | |
} | ||
} | ||
|
||
fn addr_to_sockaddr_un(addr: &CString) -> (libc::sockaddr_storage, uint) { | ||
unsafe { | ||
let storage: libc::sockaddr_storage = intrinsics::init(); | ||
let s: *mut libc::sockaddr_un = cast::transmute(&storage); | ||
(*s).sun_family = libc::AF_UNIX as libc::sa_family_t; | ||
let mut i = 0; | ||
for c in addr.iter() { | ||
(*s).sun_path[i] = c; | ||
i += 1; | ||
} | ||
|
||
let len = mem::size_of::<libc::sa_family_t>() + i + 1; //count the null terminator | ||
return (storage, len); | ||
} | ||
} | ||
|
||
fn socket(addr: ip::SocketAddr, ty: libc::c_int) -> IoResult<sock_t> { | ||
unsafe { | ||
let fam = match addr.ip { | ||
|
@@ -101,6 +118,15 @@ fn socket(addr: ip::SocketAddr, ty: libc::c_int) -> IoResult<sock_t> { | |
} | ||
} | ||
|
||
fn unix_socket(ty: libc::c_int) -> IoResult<sock_t> { | ||
unsafe { | ||
match libc::socket(libc::AF_UNIX, ty, 0) { | ||
-1 => Err(super::last_error()), | ||
fd => Ok(fd) | ||
} | ||
} | ||
} | ||
|
||
fn setsockopt<T>(fd: sock_t, opt: libc::c_int, val: libc::c_int, | ||
payload: T) -> IoResult<()> { | ||
unsafe { | ||
|
@@ -176,6 +202,25 @@ fn sockaddr_to_addr(storage: &libc::sockaddr_storage, | |
} | ||
} | ||
|
||
fn sockaddr_to_unix(storage: &libc::sockaddr_storage, | ||
len: uint) -> IoResult<CString> { | ||
match storage.ss_family as libc::c_int { | ||
libc::AF_UNIX => { | ||
assert!(len as uint <= mem::size_of::<libc::sockaddr_un>()); | ||
let storage: &libc::sockaddr_un = unsafe { | ||
cast::transmute(storage) | ||
}; | ||
unsafe { | ||
let buf:[libc::c_char, ..libc::sun_len] = storage.sun_path; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to copy |
||
Ok(CString::new(buf.as_ptr(), false)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very unsafe because there's nothing tying the lifetime of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, on second thought, a |
||
} | ||
} | ||
_ => { | ||
Err(io::standard_error(io::OtherIoError)) | ||
} | ||
} | ||
} | ||
|
||
#[cfg(unix)] | ||
pub fn init() {} | ||
|
||
|
@@ -584,3 +629,244 @@ impl rtio::RtioUdpSocket for UdpSocket { | |
impl Drop for UdpSocket { | ||
fn drop(&mut self) { unsafe { close(self.fd) } } | ||
} | ||
|
||
|
||
#[cfg(not(windows))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably get removed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should still get removed. |
||
//////////////////////////////////////////////////////////////////////////////// | ||
// Unix | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
// Unix streams | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
||
pub struct UnixStream { | ||
priv fd: sock_t, | ||
} | ||
|
||
impl UnixStream { | ||
pub fn connect(addr: &CString, ty: libc::c_int) -> IoResult<UnixStream> { | ||
// the sun_path length is limited to SUN_LEN (with null) | ||
if(addr.len() > libc::sun_len -1) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These checks should go into |
||
return Err(io::IoError { | ||
kind: io::OtherIoError, | ||
desc: "path must be smaller than SUN_LEN", | ||
detail: None, | ||
}) | ||
} | ||
unsafe { | ||
unix_socket(ty).and_then(|fd| { | ||
let (addr, len) = addr_to_sockaddr_un(addr); | ||
let ret = UnixStream{ fd: fd }; | ||
let addrp = &addr as *libc::sockaddr_storage; | ||
match retry(|| { | ||
libc::connect(fd, addrp as *libc::sockaddr, | ||
len as libc::socklen_t) | ||
}) { | ||
-1 => return Err(super::last_error()), | ||
_ => return Ok(ret) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
pub fn dgram_bind(addr: &CString) -> IoResult<UnixStream> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this should return a |
||
// the sun_path length is limited to SUN_LEN (with null) | ||
if(addr.len() > libc::sun_len - 1) { | ||
return Err(io::IoError { | ||
kind: io::OtherIoError, | ||
desc: "path must be smaller than SUN_LEN", | ||
detail: None, | ||
}) | ||
} | ||
unsafe { | ||
unix_socket(libc::SOCK_DGRAM).and_then(|fd| { | ||
let (addr, len) = addr_to_sockaddr_un(addr); | ||
let ret = UnixStream{ fd: fd }; | ||
let addrp = &addr as *libc::sockaddr_storage; | ||
match libc::bind(fd, addrp as *libc::sockaddr, | ||
len as libc::socklen_t) { | ||
-1 => return Err(super::last_error()), | ||
_ => return Ok(ret) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
pub fn fd(&self) -> sock_t { self.fd } | ||
} | ||
|
||
impl rtio::RtioPipe for UnixStream { | ||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { | ||
let ret = retry(|| { | ||
unsafe { | ||
libc::recv(self.fd, | ||
buf.as_ptr() as *mut libc::c_void, | ||
buf.len() as wrlen, | ||
0) as libc::c_int | ||
} | ||
}); | ||
if ret == 0 { | ||
Err(io::standard_error(io::EndOfFile)) | ||
} else if ret < 0 { | ||
Err(super::last_error()) | ||
} else { | ||
Ok(ret as uint) | ||
} | ||
} | ||
fn write(&mut self, buf: &[u8]) -> IoResult<()> { | ||
let ret = keep_going(buf, |buf, len| { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is necessarily correct for a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah nevermind, forgot the traits were separate. |
||
unsafe { | ||
libc::send(self.fd, | ||
buf as *mut libc::c_void, | ||
len as wrlen, | ||
0) as i64 | ||
} | ||
}); | ||
if ret < 0 { | ||
Err(super::last_error()) | ||
} else { | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
impl rtio::RtioDatagramPipe for UnixStream { | ||
fn recvfrom(&mut self, buf: &mut [u8]) -> IoResult<(uint, CString)> { | ||
unsafe { | ||
let mut storage: libc::sockaddr_storage = intrinsics::init(); | ||
let storagep = &mut storage as *mut libc::sockaddr_storage; | ||
let mut addrlen: libc::socklen_t = | ||
mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t; | ||
let ret = retry(|| { | ||
libc::recvfrom(self.fd, | ||
buf.as_ptr() as *mut libc::c_void, | ||
buf.len() as msglen_t, | ||
0, | ||
storagep as *mut libc::sockaddr, | ||
&mut addrlen) as libc::c_int | ||
}); | ||
if ret < 0 { return Err(super::last_error()) } | ||
sockaddr_to_unix(&storage, addrlen as uint).and_then(|addr| { | ||
Ok((ret as uint, addr)) | ||
}) | ||
} | ||
} | ||
|
||
fn sendto(&mut self, buf: &[u8], dst: &CString) -> IoResult<()> { | ||
let (dst, len) = addr_to_sockaddr_un(dst); | ||
let dstp = &dst as *libc::sockaddr_storage; | ||
unsafe { | ||
let ret = retry(|| { | ||
libc::sendto(self.fd, | ||
buf.as_ptr() as *libc::c_void, | ||
buf.len() as msglen_t, | ||
0, | ||
dstp as *libc::sockaddr, | ||
len as libc::socklen_t) as libc::c_int | ||
}); | ||
match ret { | ||
-1 => Err(super::last_error()), | ||
n if n as uint != buf.len() => { | ||
Err(io::IoError { | ||
kind: io::OtherIoError, | ||
desc: "couldn't send entire packet at once", | ||
detail: None, | ||
}) | ||
} | ||
_ => Ok(()) | ||
} | ||
} | ||
|
||
} | ||
} | ||
|
||
impl Drop for UnixStream { | ||
fn drop(&mut self) { unsafe { close(self.fd); } } | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
// Unix Listener | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
||
pub struct UnixListener { | ||
priv fd: sock_t, | ||
} | ||
|
||
impl UnixListener { | ||
pub fn bind(addr: &CString) -> IoResult<UnixListener> { | ||
// the sun_path length is limited to SUN_LEN (with null) | ||
if(addr.len() > libc::sun_len - 1) { | ||
return Err(io::IoError { | ||
kind: io::OtherIoError, | ||
desc: "path must be smaller than SUN_LEN", | ||
detail: None, | ||
}) | ||
} | ||
unsafe { | ||
unix_socket(libc::SOCK_STREAM).and_then(|fd| { | ||
let (addr, len) = addr_to_sockaddr_un(addr); | ||
let ret = UnixListener{ fd: fd }; | ||
let addrp = &addr as *libc::sockaddr_storage; | ||
match libc::bind(fd, addrp as *libc::sockaddr, | ||
len as libc::socklen_t) { | ||
-1 => return Err(super::last_error()), | ||
_ => return Ok(ret) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
pub fn fd(&self) -> sock_t { self.fd } | ||
|
||
pub fn native_listen(self, backlog: int) -> IoResult<UnixAcceptor> { | ||
match unsafe { libc::listen(self.fd, backlog as libc::c_int) } { | ||
-1 => Err(super::last_error()), | ||
_ => Ok(UnixAcceptor { listener: self }) | ||
} | ||
} | ||
} | ||
|
||
impl rtio::RtioUnixListener for UnixListener { | ||
fn listen(~self) -> IoResult<~rtio::RtioUnixAcceptor> { | ||
self.native_listen(128).map(|a| ~a as ~rtio::RtioUnixAcceptor) | ||
} | ||
} | ||
|
||
impl Drop for UnixListener { | ||
fn drop(&mut self) { unsafe { close(self.fd); } } | ||
} | ||
|
||
pub struct UnixAcceptor { | ||
priv listener: UnixListener, | ||
} | ||
|
||
impl UnixAcceptor { | ||
pub fn fd(&self) -> sock_t { self.listener.fd } | ||
|
||
pub fn native_accept(&mut self) -> IoResult<UnixStream> { | ||
unsafe { | ||
let mut storage: libc::sockaddr_storage = intrinsics::init(); | ||
let storagep = &mut storage as *mut libc::sockaddr_storage; | ||
let size = mem::size_of::<libc::sockaddr_storage>(); | ||
let mut size = size as libc::socklen_t; | ||
match retry(|| { | ||
libc::accept(self.fd(), | ||
storagep as *mut libc::sockaddr, | ||
&mut size as *mut libc::socklen_t) as libc::c_int | ||
}) as sock_t { | ||
-1 => Err(super::last_error()), | ||
fd => Ok(UnixStream { fd: fd }) | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl rtio::RtioUnixAcceptor for UnixAcceptor { | ||
fn accept(&mut self) -> IoResult<~rtio::RtioPipe> { | ||
self.native_accept().map(|s| ~s as ~rtio::RtioPipe) | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,12 +52,14 @@ impl UnixStream { | |
/// | ||
/// # Example | ||
/// | ||
///```rust | ||
/// use std::io::net::unix::UnixStream; | ||
/// use std::path::posix::Path; | ||
/// | ||
/// let server = Path("path/to/my/socket"); | ||
/// let server = Path::new("path/to/my/socket"); | ||
/// let mut stream = UnixStream::connect(&server); | ||
/// stream.write([1, 2, 3]); | ||
/// | ||
///``` | ||
pub fn connect<P: ToCStr>(path: &P) -> Option<UnixStream> { | ||
LocalIo::maybe_raise(|io| { | ||
io.unix_connect(&path.to_c_str()).map(UnixStream::new) | ||
|
@@ -91,6 +93,7 @@ impl UnixListener { | |
/// | ||
/// # Example | ||
/// | ||
///````rust | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this would work, but may as well switch it to three ticks instead of 4 |
||
/// use std::io::net::unix::UnixListener; | ||
/// | ||
/// let server = Path("path/to/my/socket"); | ||
|
@@ -99,7 +102,7 @@ impl UnixListener { | |
/// let mut client = client; | ||
/// client.write([1, 2, 3, 4]); | ||
/// } | ||
/// | ||
///``` | ||
pub fn bind<P: ToCStr>(path: &P) -> Option<UnixListener> { | ||
LocalIo::maybe_raise(|io| { | ||
io.unix_bind(&path.to_c_str()).map(|s| UnixListener { obj: s }) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conditions goes the wrong way, it should be that the provided length is greater than or equal to the size of
sockaddr_un
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. Why an assert, instead of an Err?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An
Err
would also be fine, I don't think the assert should reasonably trip, but might as well err on the side of caution.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this condition still needs to be reversed.