rust/src/libstd/sys/common/net.rs

965 lines
32 KiB
Rust
Raw Normal View History

2015-01-29 01:19:28 -06:00
// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
2013-12-27 19:50:16 -06:00
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use prelude::v1::*;
use self::SocketStatus::*;
use self::InAddr::*;
use ffi::CString;
use ffi;
2015-01-22 18:31:00 -06:00
use old_io::net::addrinfo;
use old_io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr};
use old_io::{IoResult, IoError};
2015-01-03 21:42:21 -06:00
use libc::{self, c_char, c_int};
use mem;
use num::Int;
2015-01-03 21:42:21 -06:00
use ptr::{self, null, null_mut};
use str;
2015-01-03 21:42:21 -06:00
use sys::{self, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock,
wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval,
decode_error_detailed};
use sync::{Arc, Mutex, MutexGuard};
2015-01-03 21:42:21 -06:00
use sys_common::{self, keep_going, short_write, timeout};
use cmp;
2015-01-22 18:31:00 -06:00
use old_io;
// FIXME: move uses of Arc and deadline tracking to std::io
2015-01-28 07:34:18 -06:00
#[derive(Debug)]
pub enum SocketStatus {
Readable,
Writable,
}
2013-12-27 19:50:16 -06:00
2013-12-28 18:40:15 -06:00
////////////////////////////////////////////////////////////////////////////////
// sockaddr and misc bindings
////////////////////////////////////////////////////////////////////////////////
2013-12-27 19:50:16 -06:00
pub fn htons(u: u16) -> u16 {
u.to_be()
2013-12-27 19:50:16 -06:00
}
pub fn ntohs(u: u16) -> u16 {
Int::from_be(u)
2013-12-27 19:50:16 -06:00
}
pub enum InAddr {
In4Addr(libc::in_addr),
2013-12-28 18:40:15 -06:00
In6Addr(libc::in6_addr),
}
pub fn ip_to_inaddr(ip: IpAddr) -> InAddr {
2013-12-28 18:40:15 -06:00
match ip {
Ipv4Addr(a, b, c, d) => {
let ip = ((a as u32) << 24) |
((b as u32) << 16) |
((c as u32) << 8) |
((d as u32) << 0);
In4Addr(libc::in_addr {
s_addr: Int::from_be(ip)
2013-12-28 18:40:15 -06:00
})
}
Ipv6Addr(a, b, c, d, e, f, g, h) => {
2013-12-28 18:40:15 -06:00
In6Addr(libc::in6_addr {
s6_addr: [
htons(a),
htons(b),
htons(c),
htons(d),
htons(e),
htons(f),
htons(g),
htons(h),
]
})
}
}
}
pub fn addr_to_sockaddr(addr: SocketAddr,
storage: &mut libc::sockaddr_storage)
-> libc::socklen_t {
2013-12-27 19:50:16 -06:00
unsafe {
2013-12-28 18:40:15 -06:00
let len = match ip_to_inaddr(addr.ip) {
In4Addr(inaddr) => {
let storage = storage as *mut _ as *mut libc::sockaddr_in;
2013-12-27 19:50:16 -06:00
(*storage).sin_family = libc::AF_INET as libc::sa_family_t;
(*storage).sin_port = htons(addr.port);
2013-12-28 18:40:15 -06:00
(*storage).sin_addr = inaddr;
2013-12-27 19:50:16 -06:00
mem::size_of::<libc::sockaddr_in>()
}
2013-12-28 18:40:15 -06:00
In6Addr(inaddr) => {
let storage = storage as *mut _ as *mut libc::sockaddr_in6;
2013-12-27 19:50:16 -06:00
(*storage).sin6_family = libc::AF_INET6 as libc::sa_family_t;
(*storage).sin6_port = htons(addr.port);
2013-12-28 18:40:15 -06:00
(*storage).sin6_addr = inaddr;
2013-12-27 19:50:16 -06:00
mem::size_of::<libc::sockaddr_in6>()
}
};
return len as libc::socklen_t;
2013-12-27 19:50:16 -06:00
}
}
pub fn socket(addr: SocketAddr, ty: libc::c_int) -> IoResult<sock_t> {
2013-12-27 19:50:16 -06:00
unsafe {
let fam = match addr.ip {
Ipv4Addr(..) => libc::AF_INET,
Ipv6Addr(..) => libc::AF_INET6,
2013-12-27 19:50:16 -06:00
};
2013-12-28 18:40:15 -06:00
match libc::socket(fam, ty, 0) {
-1 => Err(last_net_error()),
2013-12-27 19:50:16 -06:00
fd => Ok(fd),
}
}
}
pub fn setsockopt<T>(fd: sock_t, opt: libc::c_int, val: libc::c_int,
2013-12-28 18:40:15 -06:00
payload: T) -> IoResult<()> {
unsafe {
2014-06-25 14:47:34 -05:00
let payload = &payload as *const T as *const libc::c_void;
2013-12-28 18:40:15 -06:00
let ret = libc::setsockopt(fd, opt, val,
payload,
mem::size_of::<T>() as libc::socklen_t);
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
if ret != 0 {
Err(last_net_error())
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
} else {
Ok(())
}
2013-12-28 18:40:15 -06:00
}
}
pub fn getsockopt<T: Copy>(fd: sock_t, opt: libc::c_int,
val: libc::c_int) -> IoResult<T> {
unsafe {
let mut slot: T = mem::zeroed();
let mut len = mem::size_of::<T>() as libc::socklen_t;
let ret = c::getsockopt(fd, opt, val,
&mut slot as *mut _ as *mut _,
&mut len);
if ret != 0 {
Err(last_net_error())
} else {
assert!(len as uint == mem::size_of::<T>());
Ok(slot)
}
}
}
pub fn sockname(fd: sock_t,
2014-05-09 18:30:57 -05:00
f: unsafe extern "system" fn(sock_t, *mut libc::sockaddr,
2013-12-27 19:50:16 -06:00
*mut libc::socklen_t) -> libc::c_int)
-> IoResult<SocketAddr>
2013-12-27 19:50:16 -06:00
{
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
2013-12-27 19:50:16 -06:00
let mut len = mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
unsafe {
let storage = &mut storage as *mut libc::sockaddr_storage;
let ret = f(fd,
storage as *mut libc::sockaddr,
&mut len as *mut libc::socklen_t);
if ret != 0 {
return Err(last_net_error())
2013-12-27 19:50:16 -06:00
}
}
2013-12-28 18:40:15 -06:00
return sockaddr_to_addr(&storage, len as uint);
}
pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage,
len: uint) -> IoResult<SocketAddr> {
2013-12-27 19:50:16 -06:00
match storage.ss_family as libc::c_int {
libc::AF_INET => {
assert!(len as uint >= mem::size_of::<libc::sockaddr_in>());
2013-12-28 18:40:15 -06:00
let storage: &libc::sockaddr_in = unsafe {
core: Remove the cast module This commit revisits the `cast` module in libcore and libstd, and scrutinizes all functions inside of it. The result was to remove the `cast` module entirely, folding all functionality into the `mem` module. Specifically, this is the fate of each function in the `cast` module. * transmute - This function was moved to `mem`, but it is now marked as #[unstable]. This is due to planned changes to the `transmute` function and how it can be invoked (see the #[unstable] comment). For more information, see RFC 5 and #12898 * transmute_copy - This function was moved to `mem`, with clarification that is is not an error to invoke it with T/U that are different sizes, but rather that it is strongly discouraged. This function is now #[stable] * forget - This function was moved to `mem` and marked #[stable] * bump_box_refcount - This function was removed due to the deprecation of managed boxes as well as its questionable utility. * transmute_mut - This function was previously deprecated, and removed as part of this commit. * transmute_mut_unsafe - This function doesn't serve much of a purpose when it can be achieved with an `as` in safe code, so it was removed. * transmute_lifetime - This function was removed because it is likely a strong indication that code is incorrect in the first place. * transmute_mut_lifetime - This function was removed for the same reasons as `transmute_lifetime` * copy_lifetime - This function was moved to `mem`, but it is marked `#[unstable]` now due to the likelihood of being removed in the future if it is found to not be very useful. * copy_mut_lifetime - This function was also moved to `mem`, but had the same treatment as `copy_lifetime`. * copy_lifetime_vec - This function was removed because it is not used today, and its existence is not necessary with DST (copy_lifetime will suffice). In summary, the cast module was stripped down to these functions, and then the functions were moved to the `mem` module. transmute - #[unstable] transmute_copy - #[stable] forget - #[stable] copy_lifetime - #[unstable] copy_mut_lifetime - #[unstable] [breaking-change]
2014-05-09 12:34:51 -05:00
mem::transmute(storage)
2013-12-27 19:50:16 -06:00
};
let ip = (storage.sin_addr.s_addr as u32).to_be();
2014-05-27 04:00:50 -05:00
let a = (ip >> 24) as u8;
let b = (ip >> 16) as u8;
let c = (ip >> 8) as u8;
let d = (ip >> 0) as u8;
Ok(SocketAddr {
ip: Ipv4Addr(a, b, c, d),
2013-12-27 19:50:16 -06:00
port: ntohs(storage.sin_port),
})
}
libc::AF_INET6 => {
assert!(len as uint >= mem::size_of::<libc::sockaddr_in6>());
2013-12-28 18:40:15 -06:00
let storage: &libc::sockaddr_in6 = unsafe {
core: Remove the cast module This commit revisits the `cast` module in libcore and libstd, and scrutinizes all functions inside of it. The result was to remove the `cast` module entirely, folding all functionality into the `mem` module. Specifically, this is the fate of each function in the `cast` module. * transmute - This function was moved to `mem`, but it is now marked as #[unstable]. This is due to planned changes to the `transmute` function and how it can be invoked (see the #[unstable] comment). For more information, see RFC 5 and #12898 * transmute_copy - This function was moved to `mem`, with clarification that is is not an error to invoke it with T/U that are different sizes, but rather that it is strongly discouraged. This function is now #[stable] * forget - This function was moved to `mem` and marked #[stable] * bump_box_refcount - This function was removed due to the deprecation of managed boxes as well as its questionable utility. * transmute_mut - This function was previously deprecated, and removed as part of this commit. * transmute_mut_unsafe - This function doesn't serve much of a purpose when it can be achieved with an `as` in safe code, so it was removed. * transmute_lifetime - This function was removed because it is likely a strong indication that code is incorrect in the first place. * transmute_mut_lifetime - This function was removed for the same reasons as `transmute_lifetime` * copy_lifetime - This function was moved to `mem`, but it is marked `#[unstable]` now due to the likelihood of being removed in the future if it is found to not be very useful. * copy_mut_lifetime - This function was also moved to `mem`, but had the same treatment as `copy_lifetime`. * copy_lifetime_vec - This function was removed because it is not used today, and its existence is not necessary with DST (copy_lifetime will suffice). In summary, the cast module was stripped down to these functions, and then the functions were moved to the `mem` module. transmute - #[unstable] transmute_copy - #[stable] forget - #[stable] copy_lifetime - #[unstable] copy_mut_lifetime - #[unstable] [breaking-change]
2014-05-09 12:34:51 -05:00
mem::transmute(storage)
2013-12-27 19:50:16 -06:00
};
let a = ntohs(storage.sin6_addr.s6_addr[0]);
let b = ntohs(storage.sin6_addr.s6_addr[1]);
let c = ntohs(storage.sin6_addr.s6_addr[2]);
let d = ntohs(storage.sin6_addr.s6_addr[3]);
let e = ntohs(storage.sin6_addr.s6_addr[4]);
let f = ntohs(storage.sin6_addr.s6_addr[5]);
let g = ntohs(storage.sin6_addr.s6_addr[6]);
let h = ntohs(storage.sin6_addr.s6_addr[7]);
Ok(SocketAddr {
ip: Ipv6Addr(a, b, c, d, e, f, g, h),
2013-12-27 19:50:16 -06:00
port: ntohs(storage.sin6_port),
})
}
_ => {
2014-06-04 02:00:49 -05:00
Err(IoError {
2015-01-22 18:31:00 -06:00
kind: old_io::InvalidInput,
desc: "invalid argument",
2014-06-04 02:00:49 -05:00
detail: None,
})
2013-12-27 19:50:16 -06:00
}
}
}
2013-12-28 18:40:15 -06:00
////////////////////////////////////////////////////////////////////////////////
// get_host_addresses
2013-12-28 18:40:15 -06:00
////////////////////////////////////////////////////////////////////////////////
extern "system" {
fn getaddrinfo(node: *const c_char, service: *const c_char,
hints: *const libc::addrinfo,
res: *mut *mut libc::addrinfo) -> c_int;
fn freeaddrinfo(res: *mut libc::addrinfo);
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
}
pub fn get_host_addresses(host: Option<&str>, servname: Option<&str>,
hint: Option<addrinfo::Hint>)
-> Result<Vec<addrinfo::Info>, IoError>
{
sys::init_net();
assert!(host.is_some() || servname.is_some());
let c_host = host.map(|x| CString::from_slice(x.as_bytes()));
let c_host = c_host.as_ref().map(|x| x.as_ptr()).unwrap_or(null());
let c_serv = servname.map(|x| CString::from_slice(x.as_bytes()));
let c_serv = c_serv.as_ref().map(|x| x.as_ptr()).unwrap_or(null());
let hint = hint.map(|hint| {
libc::addrinfo {
ai_flags: hint.flags as c_int,
ai_family: hint.family as c_int,
ai_socktype: 0,
ai_protocol: 0,
ai_addrlen: 0,
ai_canonname: null_mut(),
ai_addr: null_mut(),
ai_next: null_mut()
}
});
let hint_ptr = hint.as_ref().map_or(null(), |x| {
x as *const libc::addrinfo
});
let mut res = null_mut();
// Make the call
let s = unsafe {
getaddrinfo(c_host, c_serv, hint_ptr, &mut res)
};
// Error?
if s != 0 {
return Err(last_gai_error(s));
}
// Collect all the results we found
let mut addrs = Vec::new();
let mut rp = res;
while !rp.is_null() {
unsafe {
let addr = try!(sockaddr_to_addr(mem::transmute((*rp).ai_addr),
(*rp).ai_addrlen as uint));
addrs.push(addrinfo::Info {
address: addr,
family: (*rp).ai_family as uint,
socktype: None,
protocol: None,
flags: (*rp).ai_flags as uint
});
rp = (*rp).ai_next as *mut libc::addrinfo;
}
}
unsafe { freeaddrinfo(res); }
Ok(addrs)
}
////////////////////////////////////////////////////////////////////////////////
// get_address_name
////////////////////////////////////////////////////////////////////////////////
extern "system" {
fn getnameinfo(sa: *const libc::sockaddr, salen: libc::socklen_t,
host: *mut c_char, hostlen: libc::size_t,
serv: *mut c_char, servlen: libc::size_t,
flags: c_int) -> c_int;
}
const NI_MAXHOST: uint = 1025;
pub fn get_address_name(addr: IpAddr) -> Result<String, IoError> {
let addr = SocketAddr{ip: addr, port: 0};
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
let len = addr_to_sockaddr(addr, &mut storage);
2014-12-31 22:40:24 -06:00
let mut hostbuf = [0 as c_char; NI_MAXHOST];
let res = unsafe {
getnameinfo(&storage as *const _ as *const libc::sockaddr, len,
hostbuf.as_mut_ptr(), NI_MAXHOST as libc::size_t,
ptr::null_mut(), 0,
0)
};
if res != 0 {
return Err(last_gai_error(res));
}
unsafe {
Ok(str::from_utf8(ffi::c_str_to_bytes(&hostbuf.as_ptr()))
.unwrap().to_string())
}
}
////////////////////////////////////////////////////////////////////////////////
// Timeout helpers
//
// The read/write functions below are the helpers for reading/writing a socket
// with a possible deadline specified. This is generally viewed as a timed out
// I/O operation.
//
// From the application's perspective, timeouts apply to the I/O object, not to
// the underlying file descriptor (it's one timeout per object). This means that
// we can't use the SO_RCVTIMEO and corresponding send timeout option.
//
// The next idea to implement timeouts would be to use nonblocking I/O. An
// invocation of select() would wait (with a timeout) for a socket to be ready.
// Once its ready, we can perform the operation. Note that the operation *must*
// be nonblocking, even though select() says the socket is ready. This is
// because some other thread could have come and stolen our data (handles can be
// cloned).
//
// To implement nonblocking I/O, the first option we have is to use the
// O_NONBLOCK flag. Remember though that this is a global setting, affecting all
// I/O objects, so this was initially viewed as unwise.
//
// It turns out that there's this nifty MSG_DONTWAIT flag which can be passed to
// send/recv, but the niftiness wears off once you realize it only works well on
// Linux [1] [2]. This means that it's pretty easy to get a nonblocking
// operation on Linux (no flag fiddling, no affecting other objects), but not on
// other platforms.
//
// To work around this constraint on other platforms, we end up using the
// original strategy of flipping the O_NONBLOCK flag. As mentioned before, this
// could cause other objects' blocking operations to suddenly become
// nonblocking. To get around this, a "blocking operation" which returns EAGAIN
// falls back to using the same code path as nonblocking operations, but with an
// infinite timeout (select + send/recv). This helps emulate blocking
// reads/writes despite the underlying descriptor being nonblocking, as well as
// optimizing the fast path of just hitting one syscall in the good case.
//
// As a final caveat, this implementation uses a mutex so only one thread is
// doing a nonblocking operation at at time. This is the operation that comes
// after the select() (at which point we think the socket is ready). This is
// done for sanity to ensure that the state of the O_NONBLOCK flag is what we
// expect (wouldn't want someone turning it on when it should be off!). All
// operations performed in the lock are *nonblocking* to avoid holding the mutex
// forever.
//
// So, in summary, Linux uses MSG_DONTWAIT and doesn't need mutexes, everyone
// else uses O_NONBLOCK and mutexes with some trickery to make sure blocking
// reads/writes are still blocking.
//
// Fun, fun!
//
// [1] http://twistedmatrix.com/pipermail/twisted-commits/2012-April/034692.html
// [2] http://stackoverflow.com/questions/19819198/does-send-msg-dontwait
2014-12-07 13:15:25 -06:00
pub fn read<T, L, R>(fd: sock_t, deadline: u64, mut lock: L, mut read: R) -> IoResult<uint> where
L: FnMut() -> T,
R: FnMut(bool) -> libc::c_int,
{
let mut ret = -1;
if deadline == 0 {
ret = retry(|| read(false));
}
if deadline != 0 || (ret == -1 && wouldblock()) {
let deadline = match deadline {
0 => None,
n => Some(n),
};
loop {
// With a timeout, first we wait for the socket to become
// readable using select(), specifying the relevant timeout for
// our previously set deadline.
2014-11-17 02:39:01 -06:00
try!(await(&[fd], deadline, Readable));
// At this point, we're still within the timeout, and we've
// determined that the socket is readable (as returned by
// select). We must still read the socket in *nonblocking* mode
// because some other thread could come steal our data. If we
// fail to read some data, we retry (hence the outer loop) and
// wait for the socket to become readable again.
let _guard = lock();
match retry(|| read(deadline.is_some())) {
-1 if wouldblock() => {}
-1 => return Err(last_net_error()),
n => { ret = n; break }
}
}
}
match ret {
0 => Err(sys_common::eof()),
n if n < 0 => Err(last_net_error()),
n => Ok(n as uint)
}
}
2014-12-07 13:15:25 -06:00
pub fn write<T, L, W>(fd: sock_t,
deadline: u64,
buf: &[u8],
write_everything: bool,
mut lock: L,
mut write: W) -> IoResult<uint> where
L: FnMut() -> T,
W: FnMut(bool, *const u8, uint) -> i64,
{
let mut ret = -1;
let mut written = 0;
if deadline == 0 {
if write_everything {
ret = keep_going(buf, |inner, len| {
written = buf.len() - len;
write(false, inner, len)
});
} else {
ret = retry(|| { write(false, buf.as_ptr(), buf.len()) });
if ret > 0 { written = ret as uint; }
}
}
if deadline != 0 || (ret == -1 && wouldblock()) {
let deadline = match deadline {
0 => None,
n => Some(n),
};
while written < buf.len() && (write_everything || written == 0) {
// As with read(), first wait for the socket to be ready for
// the I/O operation.
2014-11-17 02:39:01 -06:00
match await(&[fd], deadline, Writable) {
2015-01-22 18:31:00 -06:00
Err(ref e) if e.kind == old_io::EndOfFile && written > 0 => {
assert!(deadline.is_some());
return Err(short_write(written, "short write"))
}
Err(e) => return Err(e),
Ok(()) => {}
}
// Also as with read(), we use MSG_DONTWAIT to guard ourselves
// against unforeseen circumstances.
let _guard = lock();
2015-01-07 10:58:31 -06:00
let ptr = buf[written..].as_ptr();
let len = buf.len() - written;
match retry(|| write(deadline.is_some(), ptr, len)) {
-1 if wouldblock() => {}
-1 => return Err(last_net_error()),
n => { written += n as uint; }
}
}
ret = 0;
}
if ret < 0 {
Err(last_net_error())
} else {
Ok(written)
}
}
// See http://developerweb.net/viewtopic.php?id=3196 for where this is
// derived from.
pub fn connect_timeout(fd: sock_t,
addrp: *const libc::sockaddr,
len: libc::socklen_t,
timeout_ms: u64) -> IoResult<()> {
#[cfg(unix)] use libc::EINPROGRESS as INPROGRESS;
#[cfg(windows)] use libc::WSAEINPROGRESS as INPROGRESS;
#[cfg(unix)] use libc::EWOULDBLOCK as WOULDBLOCK;
#[cfg(windows)] use libc::WSAEWOULDBLOCK as WOULDBLOCK;
// Make sure the call to connect() doesn't block
try!(set_nonblocking(fd, true));
let ret = match unsafe { libc::connect(fd, addrp, len) } {
// If the connection is in progress, then we need to wait for it to
// finish (with a timeout). The current strategy for doing this is
// to use select() with a timeout.
-1 if os::errno() as int == INPROGRESS as int ||
os::errno() as int == WOULDBLOCK as int => {
let mut set: c::fd_set = unsafe { mem::zeroed() };
c::fd_set(&mut set, fd);
match await(fd, &mut set, timeout_ms) {
0 => Err(timeout("connection timed out")),
-1 => Err(last_net_error()),
_ => {
let err: libc::c_int = try!(
getsockopt(fd, libc::SOL_SOCKET, libc::SO_ERROR));
if err == 0 {
Ok(())
} else {
Err(decode_error_detailed(err))
}
}
}
}
-1 => Err(last_net_error()),
_ => Ok(()),
};
// be sure to turn blocking I/O back on
try!(set_nonblocking(fd, false));
return ret;
#[cfg(unix)]
fn await(fd: sock_t, set: &mut c::fd_set, timeout: u64) -> libc::c_int {
let start = timer::now();
retry(|| unsafe {
// Recalculate the timeout each iteration (it is generally
// undefined what the value of the 'tv' is after select
// returns EINTR).
let mut tv = ms_to_timeval(timeout - (timer::now() - start));
c::select(fd + 1, ptr::null_mut(), set as *mut _,
ptr::null_mut(), &mut tv)
})
}
#[cfg(windows)]
fn await(_fd: sock_t, set: &mut c::fd_set, timeout: u64) -> libc::c_int {
let mut tv = ms_to_timeval(timeout);
unsafe { c::select(1, ptr::null_mut(), set, ptr::null_mut(), &mut tv) }
}
}
pub fn await(fds: &[sock_t], deadline: Option<u64>,
status: SocketStatus) -> IoResult<()> {
let mut set: c::fd_set = unsafe { mem::zeroed() };
let mut max = 0;
2015-01-31 11:20:46 -06:00
for &fd in fds {
c::fd_set(&mut set, fd);
max = cmp::max(max, fd + 1);
}
if cfg!(windows) {
max = fds.len() as sock_t;
}
let (read, write) = match status {
Readable => (&mut set as *mut _, ptr::null_mut()),
Writable => (ptr::null_mut(), &mut set as *mut _),
};
let mut tv: libc::timeval = unsafe { mem::zeroed() };
match retry(|| {
let now = timer::now();
let tvp = match deadline {
None => ptr::null_mut(),
Some(deadline) => {
// If we're past the deadline, then pass a 0 timeout to
// select() so we can poll the status
let ms = if deadline < now {0} else {deadline - now};
tv = ms_to_timeval(ms);
&mut tv as *mut _
}
};
let r = unsafe {
c::select(max as libc::c_int, read, write, ptr::null_mut(), tvp)
};
r
}) {
-1 => Err(last_net_error()),
0 => Err(timeout("timed out")),
_ => Ok(()),
}
}
////////////////////////////////////////////////////////////////////////////////
// Basic socket representation
////////////////////////////////////////////////////////////////////////////////
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
struct Inner {
fd: sock_t,
// Unused on Linux, where this lock is not necessary.
#[allow(dead_code)]
2014-11-24 13:16:40 -06:00
lock: Mutex<()>,
}
impl Inner {
fn new(fd: sock_t) -> Inner {
2014-11-24 13:16:40 -06:00
Inner { fd: fd, lock: Mutex::new(()) }
}
}
impl Drop for Inner {
fn drop(&mut self) { unsafe { close_sock(self.fd); } }
}
pub struct Guard<'a> {
pub fd: sock_t,
2014-11-24 13:16:40 -06:00
pub guard: MutexGuard<'a, ()>,
}
#[unsafe_destructor]
impl<'a> Drop for Guard<'a> {
fn drop(&mut self) {
assert!(set_nonblocking(self.fd, false).is_ok());
}
2013-12-28 18:40:15 -06:00
}
////////////////////////////////////////////////////////////////////////////////
// TCP streams
////////////////////////////////////////////////////////////////////////////////
pub struct TcpStream {
inner: Arc<Inner>,
read_deadline: u64,
write_deadline: u64,
}
2013-12-27 19:50:16 -06:00
impl TcpStream {
pub fn connect(addr: SocketAddr, timeout: Option<u64>) -> IoResult<TcpStream> {
sys::init_net();
let fd = try!(socket(addr, libc::SOCK_STREAM));
let ret = TcpStream::new(fd);
let mut storage = unsafe { mem::zeroed() };
let len = addr_to_sockaddr(addr, &mut storage);
let addrp = &storage as *const _ as *const libc::sockaddr;
match timeout {
Some(timeout) => {
try!(connect_timeout(fd, addrp, len, timeout));
Ok(ret)
},
None => {
match retry(|| unsafe { libc::connect(fd, addrp, len) }) {
-1 => Err(last_error()),
2013-12-27 19:50:16 -06:00
_ => Ok(ret),
}
}
}
}
pub fn new(fd: sock_t) -> TcpStream {
TcpStream {
inner: Arc::new(Inner::new(fd)),
read_deadline: 0,
write_deadline: 0,
}
}
pub fn fd(&self) -> sock_t { self.inner.fd }
2013-12-27 19:50:16 -06:00
pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::IPPROTO_TCP, libc::TCP_NODELAY,
2013-12-28 18:40:15 -06:00
nodelay as libc::c_int)
2013-12-27 19:50:16 -06:00
}
pub fn set_keepalive(&mut self, seconds: Option<uint>) -> IoResult<()> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
let ret = setsockopt(self.fd(), libc::SOL_SOCKET, libc::SO_KEEPALIVE,
2013-12-28 18:40:15 -06:00
seconds.is_some() as libc::c_int);
match seconds {
Some(n) => ret.and_then(|()| self.set_tcp_keepalive(n)),
None => ret,
2013-12-27 19:50:16 -06:00
}
}
2014-09-29 02:28:50 -05:00
#[cfg(any(target_os = "macos", target_os = "ios"))]
2013-12-28 18:40:15 -06:00
fn set_tcp_keepalive(&mut self, seconds: uint) -> IoResult<()> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::IPPROTO_TCP, libc::TCP_KEEPALIVE,
2013-12-28 18:40:15 -06:00
seconds as libc::c_int)
2013-12-27 19:50:16 -06:00
}
2014-09-29 02:28:50 -05:00
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
2013-12-28 18:40:15 -06:00
fn set_tcp_keepalive(&mut self, seconds: uint) -> IoResult<()> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::IPPROTO_TCP, libc::TCP_KEEPIDLE,
2013-12-28 18:40:15 -06:00
seconds as libc::c_int)
2013-12-27 19:50:16 -06:00
}
2015-01-29 01:19:28 -06:00
#[cfg(target_os = "openbsd")]
fn set_tcp_keepalive(&mut self, seconds: uint) -> IoResult<()> {
setsockopt(self.fd(), libc::IPPROTO_TCP, libc::SO_KEEPALIVE,
seconds as libc::c_int)
}
2014-09-29 02:28:50 -05:00
#[cfg(not(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
2015-01-29 01:19:28 -06:00
target_os = "dragonfly",
target_os = "openbsd")))]
2013-12-28 18:40:15 -06:00
fn set_tcp_keepalive(&mut self, _seconds: uint) -> IoResult<()> {
2013-12-27 19:50:16 -06:00
Ok(())
}
#[cfg(target_os = "linux")]
fn lock_nonblocking(&self) {}
#[cfg(not(target_os = "linux"))]
fn lock_nonblocking<'a>(&'a self) -> Guard<'a> {
let ret = Guard {
fd: self.fd(),
2014-12-29 18:38:07 -06:00
guard: self.inner.lock.lock().unwrap(),
};
assert!(set_nonblocking(self.fd(), true).is_ok());
ret
}
2013-12-27 19:50:16 -06:00
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
let fd = self.fd();
let dolock = || self.lock_nonblocking();
let doread = |nb| unsafe {
let flags = if nb {c::MSG_DONTWAIT} else {0};
libc::recv(fd,
buf.as_mut_ptr() as *mut libc::c_void,
buf.len() as wrlen,
flags) as libc::c_int
};
read(fd, self.read_deadline, dolock, doread)
2013-12-27 19:50:16 -06:00
}
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
let fd = self.fd();
let dolock = || self.lock_nonblocking();
let dowrite = |nb: bool, buf: *const u8, len: uint| unsafe {
let flags = if nb {c::MSG_DONTWAIT} else {0};
libc::send(fd,
buf as *const _,
len as wrlen,
flags) as i64
};
write(fd, self.write_deadline, buf, true, dolock, dowrite).map(|_| ())
2013-12-27 19:50:16 -06:00
}
pub fn peer_name(&mut self) -> IoResult<SocketAddr> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
sockname(self.fd(), libc::getpeername)
2013-12-27 19:50:16 -06:00
}
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
pub fn close_write(&mut self) -> IoResult<()> {
super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_WR) })
}
pub fn close_read(&mut self) -> IoResult<()> {
super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_RD) })
}
pub fn set_timeout(&mut self, timeout: Option<u64>) {
let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
self.read_deadline = deadline;
self.write_deadline = deadline;
}
pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
2013-12-27 19:50:16 -06:00
}
pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
}
2013-12-27 19:50:16 -06:00
pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
2013-12-28 18:40:15 -06:00
sockname(self.fd(), libc::getsockname)
2013-12-27 19:50:16 -06:00
}
}
impl Clone for TcpStream {
fn clone(&self) -> TcpStream {
TcpStream {
inner: self.inner.clone(),
read_deadline: 0,
write_deadline: 0,
}
}
2013-12-27 19:50:16 -06:00
}
2013-12-28 18:40:15 -06:00
////////////////////////////////////////////////////////////////////////////////
// UDP
////////////////////////////////////////////////////////////////////////////////
pub struct UdpSocket {
inner: Arc<Inner>,
read_deadline: u64,
write_deadline: u64,
2013-12-28 18:40:15 -06:00
}
impl UdpSocket {
pub fn bind(addr: SocketAddr) -> IoResult<UdpSocket> {
sys::init_net();
let fd = try!(socket(addr, libc::SOCK_DGRAM));
let ret = UdpSocket {
inner: Arc::new(Inner::new(fd)),
read_deadline: 0,
write_deadline: 0,
};
let mut storage = unsafe { mem::zeroed() };
let len = addr_to_sockaddr(addr, &mut storage);
let addrp = &storage as *const _ as *const libc::sockaddr;
match unsafe { libc::bind(fd, addrp, len) } {
-1 => Err(last_error()),
_ => Ok(ret),
2013-12-28 18:40:15 -06:00
}
}
pub fn fd(&self) -> sock_t { self.inner.fd }
2013-12-28 18:40:15 -06:00
pub fn set_broadcast(&mut self, on: bool) -> IoResult<()> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::SOL_SOCKET, libc::SO_BROADCAST,
2013-12-28 18:40:15 -06:00
on as libc::c_int)
}
pub fn set_multicast_loop(&mut self, on: bool) -> IoResult<()> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP,
2013-12-28 18:40:15 -06:00
on as libc::c_int)
}
pub fn set_membership(&mut self, addr: IpAddr, opt: libc::c_int) -> IoResult<()> {
2013-12-28 18:40:15 -06:00
match ip_to_inaddr(addr) {
In4Addr(addr) => {
2013-12-28 18:40:15 -06:00
let mreq = libc::ip_mreq {
imr_multiaddr: addr,
// interface == INADDR_ANY
imr_interface: libc::in_addr { s_addr: 0x0 },
};
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::IPPROTO_IP, opt, mreq)
2013-12-28 18:40:15 -06:00
}
In6Addr(addr) => {
let mreq = libc::ip6_mreq {
ipv6mr_multiaddr: addr,
ipv6mr_interface: 0,
};
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::IPPROTO_IPV6, opt, mreq)
2013-12-28 18:40:15 -06:00
}
}
}
#[cfg(target_os = "linux")]
fn lock_nonblocking(&self) {}
#[cfg(not(target_os = "linux"))]
fn lock_nonblocking<'a>(&'a self) -> Guard<'a> {
let ret = Guard {
fd: self.fd(),
2014-12-29 18:38:07 -06:00
guard: self.inner.lock.lock().unwrap(),
};
assert!(set_nonblocking(self.fd(), true).is_ok());
ret
}
2013-12-28 18:40:15 -06:00
pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
2013-12-28 18:40:15 -06:00
sockname(self.fd(), libc::getsockname)
}
pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)> {
let fd = self.fd();
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
let storagep = &mut storage as *mut _ as *mut libc::sockaddr;
let mut addrlen: libc::socklen_t =
mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
let dolock = || self.lock_nonblocking();
let n = try!(read(fd, self.read_deadline, dolock, |nb| unsafe {
let flags = if nb {c::MSG_DONTWAIT} else {0};
libc::recvfrom(fd,
buf.as_mut_ptr() as *mut libc::c_void,
buf.len() as msglen_t,
flags,
storagep,
&mut addrlen) as libc::c_int
}));
sockaddr_to_addr(&storage, addrlen as uint).and_then(|addr| {
Ok((n as uint, addr))
})
2013-12-28 18:40:15 -06:00
}
pub fn send_to(&mut self, buf: &[u8], dst: SocketAddr) -> IoResult<()> {
let mut storage = unsafe { mem::zeroed() };
let dstlen = addr_to_sockaddr(dst, &mut storage);
let dstp = &storage as *const _ as *const libc::sockaddr;
let fd = self.fd();
let dolock = || self.lock_nonblocking();
let dowrite = |nb, buf: *const u8, len: uint| unsafe {
let flags = if nb {c::MSG_DONTWAIT} else {0};
libc::sendto(fd,
2014-06-25 14:47:34 -05:00
buf as *const libc::c_void,
len as msglen_t,
flags,
dstp,
dstlen) as i64
};
let n = try!(write(fd, self.write_deadline, buf, false, dolock, dowrite));
if n != buf.len() {
Err(short_write(n, "couldn't send entire packet at once"))
} else {
Ok(())
2013-12-28 18:40:15 -06:00
}
}
pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
2013-12-28 18:40:15 -06:00
match multi {
Ipv4Addr(..) => {
2013-12-28 18:40:15 -06:00
self.set_membership(multi, libc::IP_ADD_MEMBERSHIP)
}
Ipv6Addr(..) => {
2013-12-28 18:40:15 -06:00
self.set_membership(multi, libc::IPV6_ADD_MEMBERSHIP)
}
}
}
pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
2013-12-28 18:40:15 -06:00
match multi {
Ipv4Addr(..) => {
2013-12-28 18:40:15 -06:00
self.set_membership(multi, libc::IP_DROP_MEMBERSHIP)
}
Ipv6Addr(..) => {
2013-12-28 18:40:15 -06:00
self.set_membership(multi, libc::IPV6_DROP_MEMBERSHIP)
}
}
}
pub fn multicast_time_to_live(&mut self, ttl: int) -> IoResult<()> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::IPPROTO_IP, libc::IP_MULTICAST_TTL,
2013-12-28 18:40:15 -06:00
ttl as libc::c_int)
}
pub fn time_to_live(&mut self, ttl: int) -> IoResult<()> {
Implement clone() for TCP/UDP/Unix sockets This is part of the overall strategy I would like to take when approaching issue #11165. The only two I/O objects that reasonably want to be "split" are the network stream objects. Everything else can be "split" by just creating another version. The initial idea I had was the literally split the object into a reader and a writer half, but that would just introduce lots of clutter with extra interfaces that were a little unnnecssary, or it would return a ~Reader and a ~Writer which means you couldn't access things like the remote peer name or local socket name. The solution I found to be nicer was to just clone the stream itself. The clone is just a clone of the handle, nothing fancy going on at the kernel level. Conceptually I found this very easy to wrap my head around (everything else supports clone()), and it solved the "split" problem at the same time. The cloning support is pretty specific per platform/lib combination: * native/win32 - uses some specific WSA apis to clone the SOCKET handle * native/unix - uses dup() to get another file descriptor * green/all - This is where things get interesting. When we support full clones of a handle, this implies that we're allowing simultaneous writes and reads to happen. It turns out that libuv doesn't support two simultaneous reads or writes of the same object. It does support *one* read and *one* write at the same time, however. Some extra infrastructure was added to just block concurrent writers/readers until the previous read/write operation was completed. I've added tests to the tcp/unix modules to make sure that this functionality is supported everywhere.
2014-01-22 21:32:16 -06:00
setsockopt(self.fd(), libc::IPPROTO_IP, libc::IP_TTL, ttl as libc::c_int)
2013-12-28 18:40:15 -06:00
}
pub fn set_timeout(&mut self, timeout: Option<u64>) {
let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
self.read_deadline = deadline;
self.write_deadline = deadline;
}
pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
}
pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
}
}
impl Clone for UdpSocket {
fn clone(&self) -> UdpSocket {
UdpSocket {
inner: self.inner.clone(),
read_deadline: 0,
write_deadline: 0,
}
}
}