diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index 949dd08eaa343..ae643c0c03bd3 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -116,13 +116,14 @@ pub use consts::os::posix88::{S_IFDIR, S_IFIFO, S_IFMT, S_IFREG, S_IFLNK}; pub use consts::os::posix88::{S_IREAD, S_IRUSR, S_IRWXU, S_IWUSR}; pub use consts::os::posix88::{STDERR_FILENO, STDIN_FILENO, S_IXUSR}; pub use consts::os::posix88::{STDOUT_FILENO, W_OK, X_OK}; -pub use consts::os::bsd44::{AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM}; +pub use consts::os::bsd44::{AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW}; pub use consts::os::bsd44::{IPPROTO_IP, IPPROTO_IPV6, IPPROTO_TCP, TCP_NODELAY}; pub use consts::os::bsd44::{SOL_SOCKET, SO_KEEPALIVE, SO_ERROR}; pub use consts::os::bsd44::{SO_REUSEADDR, SO_BROADCAST, SHUT_WR, IP_MULTICAST_LOOP}; pub use consts::os::bsd44::{IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP}; pub use consts::os::bsd44::{IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP}; -pub use consts::os::bsd44::{IP_MULTICAST_TTL, IP_TTL, SHUT_RD}; +pub use consts::os::bsd44::{IP_MULTICAST_TTL, IP_TTL, IP_HDRINCL, SHUT_RD}; +pub use consts::os::extra::{IPPROTO_RAW}; pub use funcs::c95::ctype::{isalnum, isalpha, iscntrl, isdigit}; pub use funcs::c95::ctype::{islower, isprint, ispunct, isspace}; @@ -178,14 +179,16 @@ pub use funcs::bsd43::{shutdown}; #[cfg(unix)] pub use consts::os::posix88::{ECANCELED, SIGINT, EINPROGRESS}; #[cfg(unix)] pub use consts::os::posix88::{ENOSYS, ENOTTY, ETIMEDOUT, EMFILE}; #[cfg(unix)] pub use consts::os::posix88::{SIGTERM, SIGKILL, SIGPIPE, PROT_NONE}; -#[cfg(unix)] pub use consts::os::posix01::{SIG_IGN}; +#[cfg(unix)] pub use consts::os::posix01::{SIG_IGN, F_GETFL, F_SETFL}; #[cfg(unix)] pub use consts::os::bsd44::{AF_UNIX}; +#[cfg(unix)] pub use consts::os::extra::{O_NONBLOCK}; #[cfg(unix)] pub use types::os::common::posix01::{pthread_t, timespec, timezone}; #[cfg(unix)] pub use types::os::arch::posix88::{uid_t, gid_t}; #[cfg(unix)] pub use types::os::arch::posix01::{pthread_attr_t}; #[cfg(unix)] pub use types::os::arch::posix01::{stat, utimbuf}; +#[cfg(unix)] pub use types::os::common::bsd44::{ifaddrs}; #[cfg(unix)] pub use funcs::posix88::unistd::{sysconf, setgid, setsid, setuid, pread, pwrite}; #[cfg(unix)] pub use funcs::posix88::unistd::{getgid, getuid}; #[cfg(unix)] pub use funcs::posix88::unistd::{_PC_NAME_MAX, utime, nanosleep, pathconf, link}; @@ -193,9 +196,11 @@ pub use funcs::bsd43::{shutdown}; #[cfg(unix)] pub use funcs::posix88::mman::{mmap, munmap, mprotect}; #[cfg(unix)] pub use funcs::posix88::dirent::{opendir, readdir_r, closedir}; #[cfg(unix)] pub use funcs::posix88::fcntl::{fcntl}; +#[cfg(unix)] pub use funcs::posix88::net::{if_nametoindex}; #[cfg(unix)] pub use funcs::posix01::stat_::{lstat}; #[cfg(unix)] pub use funcs::posix01::unistd::{fsync, ftruncate}; #[cfg(unix)] pub use funcs::posix01::unistd::{readlink, symlink}; +#[cfg(unix)] pub use funcs::bsd43::{getifaddrs, freeifaddrs}; #[cfg(windows)] pub use consts::os::c95::{WSAECONNREFUSED, WSAECONNRESET, WSAEACCES}; #[cfg(windows)] pub use consts::os::c95::{WSAEWOULDBLOCK, WSAENOTCONN, WSAECONNABORTED}; @@ -236,6 +241,7 @@ pub use funcs::bsd43::{shutdown}; #[cfg(windows)] pub use consts::os::extra::{ERROR_PIPE_CONNECTED, WAIT_OBJECT_0}; #[cfg(windows)] pub use consts::os::extra::{ERROR_NOT_FOUND}; #[cfg(windows)] pub use consts::os::extra::{ERROR_OPERATION_ABORTED}; +#[cfg(windows)] pub use consts::os::extra::{FIONBIO}; #[cfg(windows)] pub use types::os::common::bsd44::{SOCKET}; #[cfg(windows)] pub use types::os::common::posix01::{stat, utimbuf}; #[cfg(windows)] pub use types::os::arch::extra::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES}; @@ -268,6 +274,7 @@ pub use funcs::bsd43::{shutdown}; #[cfg(windows)] pub use funcs::extra::kernel32::{DisconnectNamedPipe, OpenProcess}; #[cfg(windows)] pub use funcs::extra::kernel32::{MoveFileExW, VirtualProtect}; #[cfg(windows)] pub use funcs::extra::msvcrt::{get_osfhandle, open_osfhandle}; +#[cfg(windows)] pub use funcs::extra::winsock::{ioctlsocket}; #[cfg(target_os = "linux")] #[cfg(target_os = "android")] #[cfg(target_os = "freebsd")] #[cfg(target_os = "dragonfly")] @@ -275,6 +282,10 @@ pub use consts::os::posix01::{CLOCK_REALTIME, CLOCK_MONOTONIC}; #[cfg(target_os = "linux")] #[cfg(target_os = "android")] pub use funcs::posix01::unistd::{fdatasync}; +#[cfg(target_os = "linux")] #[cfg(target_os = "android")] +pub use types::os::arch::extra::{sockaddr_ll}; +#[cfg(target_os = "linux")] #[cfg(target_os = "android")] +pub use consts::os::extra::{AF_PACKET}; #[cfg(unix, not(target_os = "freebsd"))] pub use consts::os::extra::{MAP_STACK}; @@ -400,6 +411,7 @@ pub mod types { pub type sighandler_t = size_t; } pub mod bsd44 { + use types::common::c95::{c_void}; use types::os::arch::c95::{c_char, c_int, c_uint}; pub type socklen_t = u32; @@ -477,6 +489,18 @@ pub mod types { pub sun_family: sa_family_t, pub sun_path: [c_char, ..108] } + + #[repr(C)] + pub struct ifaddrs { + pub ifa_next: *mut ifaddrs, + pub ifa_name: *mut c_char, + pub ifa_flags: c_uint, + pub ifa_addr: *mut sockaddr, + pub ifa_netmask: *mut sockaddr, + pub ifa_ifu: *mut sockaddr, // FIXME This should be a union + pub ifa_data: *mut c_void + } + } } @@ -675,7 +699,20 @@ pub mod types { } pub mod posix08 {} pub mod bsd44 {} - pub mod extra {} + pub mod extra { + use types::os::arch::c95::{c_ushort, c_int, c_uchar}; + #[repr(C)] + pub struct sockaddr_ll { + pub sll_family: c_ushort, + pub sll_protocol: c_ushort, + pub sll_ifindex: c_int, + pub sll_hatype: c_ushort, + pub sll_pkttype: c_uchar, + pub sll_halen: c_uchar, + pub sll_addr: [c_uchar, ..8] + } + } + } #[cfg(target_arch = "x86_64")] @@ -763,6 +800,17 @@ pub mod types { pub mod bsd44 { } pub mod extra { + use types::os::arch::c95::{c_ushort, c_int, c_uchar}; + pub struct sockaddr_ll { + pub sll_family: c_ushort, + pub sll_protocol: c_ushort, + pub sll_ifindex: c_int, + pub sll_hatype: c_ushort, + pub sll_pkttype: c_uchar, + pub sll_halen: c_uchar, + pub sll_addr: [c_uchar, ..8] + } + } } } @@ -812,6 +860,7 @@ pub mod types { pub type sighandler_t = size_t; } pub mod bsd44 { + use types::common::c95::{c_void}; use types::os::arch::c95::{c_char, c_int, c_uint}; pub type socklen_t = u32; @@ -884,6 +933,18 @@ pub mod types { pub sun_family: sa_family_t, pub sun_path: [c_char, ..104] } + #[repr(C)] + pub struct ifaddrs { + pub ifa_next: *mut ifaddrs, + pub ifa_name: *mut c_char, + pub ifa_flags: c_uint, + pub ifa_addr: *mut sockaddr, + pub ifa_netmask: *mut sockaddr, + pub ifa_dstaddr: *mut sockaddr, + pub ifa_data: *mut c_void + } + + } } @@ -1628,6 +1689,7 @@ pub mod types { } pub mod bsd44 { + use types::common::c95::{c_void}; use types::os::arch::c95::{c_char, c_int, c_uint}; pub type socklen_t = c_int; @@ -1700,6 +1762,16 @@ pub mod types { pub sun_family: sa_family_t, pub sun_path: [c_char, ..104] } + #[repr(C)] + pub struct ifaddrs { + pub ifa_next: *mut ifaddrs, + pub ifa_name: *mut c_char, + pub ifa_flags: c_uint, + pub ifa_addr: *mut sockaddr, + pub ifa_netmask: *mut sockaddr, + pub ifa_dstaddr: *mut sockaddr, + pub ifa_data: *mut c_void + } } } @@ -2032,6 +2104,7 @@ pub mod consts { pub static AF_INET6: c_int = 23; pub static SOCK_STREAM: c_int = 1; pub static SOCK_DGRAM: c_int = 2; + pub static SOCK_RAW: c_int = 3; pub static IPPROTO_TCP: c_int = 6; pub static IPPROTO_IP: c_int = 0; pub static IPPROTO_IPV6: c_int = 41; @@ -2042,6 +2115,7 @@ pub mod consts { pub static IPV6_ADD_MEMBERSHIP: c_int = 5; pub static IPV6_DROP_MEMBERSHIP: c_int = 6; pub static IP_TTL: c_int = 4; + pub static IP_HDRINCL: c_int = 2; pub static TCP_NODELAY: c_int = 0x0001; pub static SOL_SOCKET: c_int = 0xffff; @@ -2050,12 +2124,14 @@ pub mod consts { pub static SO_REUSEADDR: c_int = 4; pub static SO_ERROR: c_int = 0x1007; + pub static IFF_LOOPBACK: c_int = 4; + pub static SHUT_RD: c_int = 0; pub static SHUT_WR: c_int = 1; pub static SHUT_RDWR: c_int = 2; } pub mod extra { - use types::os::arch::c95::c_int; + use types::os::arch::c95::{c_int, c_long}; use types::os::arch::extra::{WORD, DWORD, BOOL, HANDLE}; pub static TRUE : BOOL = 1; @@ -2279,6 +2355,10 @@ pub mod consts { pub static PIPE_ACCEPT_REMOTE_CLIENTS: DWORD = 0x00000000; pub static PIPE_REJECT_REMOTE_CLIENTS: DWORD = 0x00000008; pub static PIPE_UNLIMITED_INSTANCES: DWORD = 255; + + pub static IPPROTO_RAW: c_int = 255; + + pub static FIONBIO: c_long = -0x7FFB9982; } pub mod sysconf { } @@ -2737,6 +2817,12 @@ pub mod consts { pub mod posix01 { use types::os::arch::c95::{c_int, size_t}; + pub static F_DUPFD : c_int = 0; + pub static F_GETFD : c_int = 1; + pub static F_SETFD : c_int = 2; + pub static F_GETFL : c_int = 3; + pub static F_SETFL : c_int = 4; + pub static SIGTRAP : c_int = 5; pub static SIGPIPE: c_int = 13; pub static SIG_IGN: size_t = 1; @@ -2826,17 +2912,21 @@ pub mod consts { pub static MADV_UNMERGEABLE : c_int = 13; pub static MADV_HWPOISON : c_int = 100; + pub static IFF_LOOPBACK: c_int = 0x8; + pub static AF_UNIX: c_int = 1; pub static AF_INET: c_int = 2; pub static AF_INET6: c_int = 10; pub static SOCK_STREAM: c_int = 1; pub static SOCK_DGRAM: c_int = 2; + pub static SOCK_RAW: c_int = 3; pub static IPPROTO_TCP: c_int = 6; pub static IPPROTO_IP: c_int = 0; pub static IPPROTO_IPV6: c_int = 41; pub static IP_MULTICAST_TTL: c_int = 33; pub static IP_MULTICAST_LOOP: c_int = 34; pub static IP_TTL: c_int = 2; + pub static IP_HDRINCL: c_int = 3; pub static IP_ADD_MEMBERSHIP: c_int = 35; pub static IP_DROP_MEMBERSHIP: c_int = 36; pub static IPV6_ADD_MEMBERSHIP: c_int = 20; @@ -2903,8 +2993,12 @@ pub mod consts { pub mod extra { use types::os::arch::c95::c_int; + pub static AF_PACKET : c_int = 17; + pub static IPPROTO_RAW : c_int = 255; + pub static O_RSYNC : c_int = 1052672; pub static O_DSYNC : c_int = 4096; + pub static O_NONBLOCK : c_int = 2048; pub static O_SYNC : c_int = 1052672; pub static PROT_GROWSDOWN : c_int = 0x010000000; @@ -3240,6 +3334,12 @@ pub mod consts { pub mod posix01 { use types::os::arch::c95::{c_int, size_t}; + pub static F_DUPFD : c_int = 0; + pub static F_GETFD : c_int = 1; + pub static F_SETFD : c_int = 2; + pub static F_GETFL : c_int = 3; + pub static F_SETFL : c_int = 4; + pub static SIGTRAP : c_int = 5; pub static SIGPIPE: c_int = 13; pub static SIG_IGN: size_t = 1; @@ -3338,12 +3438,14 @@ pub mod consts { pub static AF_UNIX: c_int = 1; pub static SOCK_STREAM: c_int = 1; pub static SOCK_DGRAM: c_int = 2; + pub static SOCK_RAW: c_int = 3; pub static IPPROTO_TCP: c_int = 6; pub static IPPROTO_IP: c_int = 0; pub static IPPROTO_IPV6: c_int = 41; pub static IP_MULTICAST_TTL: c_int = 10; pub static IP_MULTICAST_LOOP: c_int = 11; pub static IP_TTL: c_int = 4; + pub static IP_HDRINCL: c_int = 2; pub static IP_ADD_MEMBERSHIP: c_int = 12; pub static IP_DROP_MEMBERSHIP: c_int = 13; pub static IPV6_ADD_MEMBERSHIP: c_int = 12; @@ -3357,6 +3459,8 @@ pub mod consts { pub static SO_REUSEADDR: c_int = 0x0004; pub static SO_ERROR: c_int = 0x1007; + pub static IFF_LOOPBACK: c_int = 0x8; + pub static SHUT_RD: c_int = 0; pub static SHUT_WR: c_int = 1; pub static SHUT_RDWR: c_int = 2; @@ -3365,6 +3469,7 @@ pub mod consts { use types::os::arch::c95::c_int; pub static O_SYNC : c_int = 128; + pub static O_NONBLOCK : c_int = 4; pub static CTL_KERN: c_int = 1; pub static KERN_PROC: c_int = 14; #[cfg(target_os = "freebsd")] @@ -3379,6 +3484,8 @@ pub mod consts { pub static MAP_STACK : c_int = 0x0400; pub static MAP_NOSYNC : c_int = 0x0800; pub static MAP_NOCORE : c_int = 0x020000; + + pub static IPPROTO_RAW : c_int = 255; } pub mod sysconf { use types::os::arch::c95::c_int; @@ -3646,6 +3753,12 @@ pub mod consts { pub mod posix01 { use types::os::arch::c95::{c_int, size_t}; + pub static F_DUPFD : c_int = 0; + pub static F_GETFD : c_int = 1; + pub static F_SETFD : c_int = 2; + pub static F_GETFL : c_int = 3; + pub static F_SETFL : c_int = 4; + pub static SIGTRAP : c_int = 5; pub static SIGPIPE: c_int = 13; pub static SIG_IGN: size_t = 1; @@ -3728,12 +3841,14 @@ pub mod consts { pub static AF_INET6: c_int = 30; pub static SOCK_STREAM: c_int = 1; pub static SOCK_DGRAM: c_int = 2; + pub static SOCK_RAW: c_int = 3; pub static IPPROTO_TCP: c_int = 6; pub static IPPROTO_IP: c_int = 0; pub static IPPROTO_IPV6: c_int = 41; pub static IP_MULTICAST_TTL: c_int = 10; pub static IP_MULTICAST_LOOP: c_int = 11; pub static IP_TTL: c_int = 4; + pub static IP_HDRINCL: c_int = 2; pub static IP_ADD_MEMBERSHIP: c_int = 12; pub static IP_DROP_MEMBERSHIP: c_int = 13; pub static IPV6_ADD_MEMBERSHIP: c_int = 12; @@ -3747,6 +3862,8 @@ pub mod consts { pub static SO_REUSEADDR: c_int = 0x0004; pub static SO_ERROR: c_int = 0x1007; + pub static IFF_LOOPBACK: c_int = 0x8; + pub static SHUT_RD: c_int = 0; pub static SHUT_WR: c_int = 1; pub static SHUT_RDWR: c_int = 2; @@ -3756,6 +3873,7 @@ pub mod consts { pub static O_DSYNC : c_int = 4194304; pub static O_SYNC : c_int = 128; + pub static O_NONBLOCK : c_int = 4; pub static F_FULLFSYNC : c_int = 51; pub static MAP_COPY : c_int = 0x0002; @@ -3766,6 +3884,8 @@ pub mod consts { pub static MAP_NOCACHE : c_int = 0x0400; pub static MAP_JIT : c_int = 0x0800; pub static MAP_STACK : c_int = 0; + + pub static IPPROTO_RAW : c_int = 255; } pub mod sysconf { use types::os::arch::c95::c_int; @@ -4326,6 +4446,15 @@ pub mod funcs { pub fn shm_unlink(name: *const c_char) -> c_int; } } + + pub mod net { + use types::os::arch::c95::{c_char, c_uint}; + + extern { + pub fn if_nametoindex(ifname: *const c_char) -> c_uint; + } + } + } #[cfg(target_os = "linux")] @@ -4439,6 +4568,9 @@ pub mod funcs { pub mod mman { } + + pub mod net { + } } @@ -4457,7 +4589,7 @@ pub mod funcs { #[cfg(not(windows))] pub mod bsd43 { use types::common::c95::{c_void}; - use types::os::common::bsd44::{socklen_t, sockaddr}; + use types::os::common::bsd44::{socklen_t, sockaddr, ifaddrs}; use types::os::arch::c95::{c_int, size_t}; use types::os::arch::posix88::ssize_t; @@ -4487,6 +4619,8 @@ pub mod funcs { pub fn sendto(socket: c_int, buf: *const c_void, len: size_t, flags: c_int, addr: *const sockaddr, addrlen: socklen_t) -> ssize_t; + pub fn getifaddrs(ifap: *mut *mut ifaddrs) -> c_int; + pub fn freeifaddrs(ifa: *mut ifaddrs); pub fn shutdown(socket: c_int, how: c_int) -> c_int; } } @@ -4572,6 +4706,7 @@ pub mod funcs { extern { pub fn getdtablesize() -> c_int; + pub fn ioctl(d: c_int, request: c_int, ...) -> c_int; pub fn madvise(addr: *mut c_void, len: size_t, advice: c_int) -> c_int; pub fn mincore(addr: *mut c_void, len: size_t, vec: *mut c_uchar) @@ -4815,6 +4950,15 @@ pub mod funcs { flags: c_int) -> c_int; } } + + pub mod winsock { + use types::os::arch::c95::{c_int, c_long, c_ulong}; + use types::os::common::bsd44::SOCKET; + + extern "system" { + pub fn ioctlsocket(s: SOCKET, cmd: c_long, argp: *mut c_ulong) -> c_int; + } + } } } diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index 2dc6539b17806..43257c1697098 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -26,6 +26,7 @@ use libc::c_int; use libc; use std::c_str::CString; +use std::num::from_i32; use std::os; use std::rt::rtio; use std::rt::rtio::{IoResult, IoError}; @@ -117,22 +118,24 @@ fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { #[cfg(windows)] #[inline] -fn retry(f: || -> libc::c_int) -> libc::c_int { +fn retry(f: || -> T) -> T { loop { - match f() { - -1 if os::errno() as int == libc::WSAEINTR as int => {} - n => return n, + let minus1: T = from_i32(-1).unwrap(); + let ret = f(); + if ret != minus1 || os::errno() as int != libc::WSAEINTR as int { + return ret } } } #[cfg(unix)] #[inline] -fn retry(f: || -> libc::c_int) -> libc::c_int { +fn retry(f: || -> T) -> T { loop { - match f() { - -1 if os::errno() as int == libc::EINTR as int => {} - n => return n, + let minus1: T = from_i32(-1).unwrap(); + let ret = f(); + if ret != minus1 || os::errno() as int != libc::EINTR as int { + return ret } } } @@ -208,6 +211,10 @@ impl rtio::IoFactory for IoFactory { { addrinfo::GetAddrInfoRequest::run(host, servname, hint) } + fn socket_from_raw_fd(&mut self, fd: net::sock_t) + -> IoResult> { + net::Socket::new(fd).map(|s| box s as Box) + } // filesystem operations fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior) diff --git a/src/libnative/io/net.rs b/src/libnative/io/net.rs index 368b5914444ac..dadcbeec70d4a 100644 --- a/src/libnative/io/net.rs +++ b/src/libnative/io/net.rs @@ -877,6 +877,75 @@ impl rtio::RtioUdpSocket for UdpSocket { } } +//////////////////////////////////////////////////////////////////////////////// +/// Arbitrary sockets +//////////////////////////////////////////////////////////////////////////////// + +#[cfg(windows)] +type Buflen = i32; +#[cfg(not(windows))] +type Buflen = u64; + +pub struct Socket { + fd: sock_t, +} + +impl Socket { + pub fn new(sock: sock_t) -> IoResult + { + let socket = Socket { fd: sock }; + return Ok(socket); + } +} + +impl rtio::RtioCustomSocket for Socket { + fn recv_from(&mut self, buf: &mut [u8], addr: *mut libc::sockaddr_storage) + -> IoResult + { + let mut caddrlen = mem::size_of::() as libc::socklen_t; + let len = unsafe { + retry( || libc::recvfrom(self.fd, buf.as_ptr() as *mut libc::c_void, + buf.len() as Buflen, 0, addr as *mut libc::sockaddr, + &mut caddrlen)) + }; + if len == -1 { + return Err(last_error()); + } + + return Ok(len as uint); + } + + fn send_to(&mut self, buf: &[u8], addr: *const libc::sockaddr, slen: uint) + -> IoResult + { + let len = unsafe { + retry( || libc::sendto(self.fd, buf.as_ptr() as *const libc::c_void, + buf.len() as Buflen, 0, addr, slen as libc::socklen_t)) + }; + + return if len < 0 { + Err(last_error()) + } else { + Ok(len as uint) + }; + } + + fn clone(&self) -> Box { + box Socket { + fd: self.fd + } as Box + } + +} + +impl Drop for Socket { + fn drop(&mut self) { + unsafe { + os::close(self.fd) + } + } +} + //////////////////////////////////////////////////////////////////////////////// // Timeout helpers // diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs index 261d544a24149..8151153170ac4 100644 --- a/src/librustrt/rtio.rs +++ b/src/librustrt/rtio.rs @@ -23,6 +23,9 @@ use c_str::CString; use local::Local; use task::Task; +#[cfg(windows)] pub type CSocketT = libc::SOCKET; +#[cfg(unix)] pub type CSocketT = libc::c_int; + pub trait EventLoop { fn run(&mut self); fn callback(&mut self, arg: proc(): Send); @@ -201,6 +204,8 @@ pub trait IoFactory { fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>, hint: Option) -> IoResult>; + fn socket_from_raw_fd(&mut self, fd: CSocketT) + -> IoResult>; // filesystem operations fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior) @@ -292,6 +297,12 @@ pub trait RtioUdpSocket : RtioSocket { fn set_write_timeout(&mut self, timeout_ms: Option); } +pub trait RtioCustomSocket { + fn recv_from(&mut self, buf: &mut [u8], *mut libc::sockaddr_storage) -> IoResult; + fn send_to(&mut self, buf: &[u8], dst: *const libc::sockaddr, len: uint) -> IoResult; + fn clone(&self) -> Box; +} + pub trait RtioTimer { fn sleep(&mut self, msecs: u64); fn oneshot(&mut self, msecs: u64, cb: Box); diff --git a/src/librustuv/net.rs b/src/librustuv/net.rs index 84ef9deaf922f..35bce92f2240f 100644 --- a/src/librustuv/net.rs +++ b/src/librustuv/net.rs @@ -840,6 +840,277 @@ impl Drop for UdpWatcher { } } +//////////////////////////////////////////////////////////////////////////////// +/// Raw socket implementation +//////////////////////////////////////////////////////////////////////////////// + +pub struct SocketWatcher { + handle: *mut uvll::uv_poll_t, + socket: uvll::uv_os_socket_t, + home: HomeHandle, + + // See above for what these are + refcount: Refcount, + read_access: AccessTimeout<()>, + write_access: AccessTimeout<()>, + +} + +#[cfg(windows)] +type Buflen = i32; +#[cfg(not(windows))] +type Buflen = u64; + +#[cfg(windows)] +fn last_error() -> IoError { + use std::os; + let code = unsafe { libc::GetLastError() as uint }; + IoError { + code: code, + extra: 0, + detail: Some(os::error_string(code)), + } +} + +#[cfg(not(windows))] +fn last_error() -> IoError { + use std::os; + + let errno = os::errno() as uint; + IoError { + code: os::errno() as uint, + extra: 0, + detail: Some(os::error_string(errno)), + } +} + + +#[cfg(windows)] +fn make_nonblocking(socket: libc::SOCKET) -> Option { + let mut one: libc::c_ulong = 1; + if unsafe { libc::ioctlsocket(socket, libc::FIONBIO, &mut one as *mut libc::c_ulong) } != 0 { + Some(last_error()) + } else { + None + } +} + +#[cfg(not(windows))] +fn make_nonblocking(socket: c_int) -> Option { + let flags = unsafe { libc::fcntl(socket, libc::F_GETFL, 0i) }; + if flags == -1 { + return Some(last_error()); + } + if unsafe { libc::fcntl(socket, libc::F_SETFL, flags | libc::O_NONBLOCK) } == -1 { + return Some(last_error()); + } + return None; +} + +impl SocketWatcher { + // NOTE It is an error to have multiple SocketWatchers for the same socket, + // see documentation for uv_poll_s in uv.h. + pub fn new(io: &mut UvIoFactory, socket: uvll::uv_os_socket_t) + -> Result + { + let handle = unsafe { uvll::malloc_handle(uvll::UV_POLL) }; + + let raw = SocketWatcher { + handle: handle, + home: io.make_handle(), + socket: socket, + refcount: Refcount::new(), + read_access: AccessTimeout::new(()), + write_access: AccessTimeout::new(()), + }; + + // Make socket non-blocking - required for libuv + match make_nonblocking(raw.socket) { + Some(e) => return Err(e), + None => () + } + + assert_eq!(unsafe { + uvll::uv_poll_init_socket(io.uv_loop(), raw.handle, raw.socket) + }, 0); + return Ok(raw); + } +} + +impl UvHandle for SocketWatcher { + fn uv_handle(&self) -> *mut uvll::uv_poll_t { self.handle } +} + +impl Drop for SocketWatcher { + fn drop(&mut self) { + let _m = self.fire_homing_missile(); + if self.refcount.decrement() { + self.close(); + } + } +} + +impl HomingIO for SocketWatcher { + fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home } +} + +impl rtio::RtioCustomSocket for SocketWatcher { + fn recv_from(&mut self, buf: &mut [u8], addr: *mut libc::sockaddr_storage) + -> Result + { + struct Ctx<'b> { + task: Option, + buf: &'b [u8], + result: Option>, + socket: uvll::uv_os_socket_t, + addr: *mut libc::sockaddr_storage + } + let loop_ = self.uv_loop(); + let m = self.fire_homing_missile(); + let _guard = try!(self.read_access.grant(m)); + let a = match unsafe { + uvll::uv_poll_start(self.handle, uvll::UV_READABLE as c_int, recv_cb) + } { + 0 => { + let mut cx = Ctx { + task: None, + buf: buf, + result: None, + socket: self.socket, + addr: addr, + }; + let handle = self.handle; + wait_until_woken_after(&mut cx.task, &loop_, || { + unsafe { uvll::set_data_for_uv_handle(handle, &mut cx) } + }); + cx.result.unwrap().map(|n| n as uint) + } + n => Err(uv_error_to_io_error(UvError(n))) + }; + return a; + + extern fn recv_cb(handle: *mut uvll::uv_poll_t, status: c_int, events: c_int) { + assert!((events & (uvll::UV_READABLE as c_int)) != 0); + let cx = unsafe { + uvll::get_data_for_uv_handle(handle) as *mut Ctx + }; + + if status < 0 { + unsafe { + (*cx).result = Some(Err(uv_error_to_io_error(UvError(status)))); + wakeup(&mut (*cx).task); + } + return; + } + + unsafe { + assert_eq!(uvll::uv_poll_stop(handle), 0) + } + + let mut caddrlen = mem::size_of::() as libc::socklen_t; + let len = unsafe { + libc::recvfrom((*cx).socket, (*cx).buf.as_ptr() as *mut c_void, + (*cx).buf.len() as Buflen, 0, + (*cx).addr as *mut libc::sockaddr, &mut caddrlen) + }; + + unsafe { + (*cx).result = if len == -1 { + Some(Err(last_error())) + } else { + Some(Ok(len as ssize_t)) + }; + + wakeup(&mut (*cx).task); + } + } + } + + fn send_to(&mut self, buf: &[u8], dst: *const libc::sockaddr, slen: uint) + -> Result + { + struct Ctx<'b> { + task: Option, + buf: &'b [u8], + result: Option>, + socket: uvll::uv_os_socket_t, + addr: *const libc::sockaddr, + len: uint + } + let loop_ = self.uv_loop(); + let m = self.fire_homing_missile(); + let _guard = try!(self.write_access.grant(m)); + + let a = match unsafe { + uvll::uv_poll_start(self.handle, uvll::UV_WRITABLE as c_int, send_cb) + } { + 0 => { + let mut cx = Ctx { + task: None, + buf: buf, + result: None, + socket: self.socket, + addr: dst, + len: slen + }; + let handle = self.handle; + wait_until_woken_after(&mut cx.task, &loop_, || { + unsafe { uvll::set_data_for_uv_handle(handle, &mut cx) } + }); + cx.result.unwrap() + } + n => Err(uv_error_to_io_error(UvError(n))) + }; + return a; + + extern fn send_cb(handle: *mut uvll::uv_poll_t, status: c_int, events: c_int) { + assert!((events & (uvll::UV_WRITABLE as c_int)) != 0); + let cx = unsafe { + uvll::get_data_for_uv_handle(handle) as *mut Ctx + }; + if status < 0 { + unsafe { + (*cx).result = Some(Err(uv_error_to_io_error(UvError(status)))); + wakeup(&mut (*cx).task); + } + return; + } + + unsafe { + assert_eq!(uvll::uv_poll_stop(handle), 0) + } + + let len = unsafe { + libc::sendto((*cx).socket, (*cx).buf.as_ptr() as *const c_void, + (*cx).buf.len() as Buflen, 0, + (*cx).addr, (*cx).len as libc::socklen_t) + }; + + unsafe { + (*cx).result = if len < 0 { + Some(Err(last_error())) + } else { + Some(Ok(len as uint)) + }; + + wakeup(&mut (*cx).task); + } + } + } + + fn clone(&self) -> Box { + box SocketWatcher { + handle: self.handle, + socket: self.socket, + home: self.home.clone(), + refcount: self.refcount.clone(), + write_access: self.write_access.clone(), + read_access: self.read_access.clone(), + } as Box + } +} + + //////////////////////////////////////////////////////////////////////////////// // Shutdown helper //////////////////////////////////////////////////////////////////////////////// diff --git a/src/librustuv/uvio.rs b/src/librustuv/uvio.rs index 61e52a3abd19c..f420d5fb01c02 100644 --- a/src/librustuv/uvio.rs +++ b/src/librustuv/uvio.rs @@ -29,7 +29,7 @@ use file::{FsRequest, FileWatcher}; use queue::QueuePool; use homing::HomeHandle; use idle::IdleWatcher; -use net::{TcpWatcher, TcpListener, UdpWatcher}; +use net::{SocketWatcher, TcpWatcher, TcpListener, UdpWatcher}; use pipe::{PipeWatcher, PipeListener}; use process::Process; use signal::SignalWatcher; @@ -176,6 +176,11 @@ impl IoFactory for UvIoFactory { r.map_err(uv_error_to_io_error) } + fn socket_from_raw_fd(&mut self, fd: uvll::uv_os_socket_t) + -> IoResult> { + SocketWatcher::new(self, fd).map(|sw| box sw as Box) + } + fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior) -> Box { box FileWatcher::new(self, fd, close) as diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index dc6478df3605d..b21af143f2282 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -412,7 +412,10 @@ impl IoError { IoError::from_errno(os::errno() as uint, true) } - fn from_rtio_error(err: rtio::IoError) -> IoError { + /// Convert a from a runtime I/O error to a IoError + /// + /// Primarily useful when using `IoFactory::socket_from_raw_fd()`. + pub fn from_rtio_error(err: rtio::IoError) -> IoError { let rtio::IoError { code, extra, detail } = err; let mut ioerr = IoError::from_errno(code, false); ioerr.detail = detail;