diff --git a/src/sys/socket.rs b/src/sys/socket.rs deleted file mode 100644 index 6450ff2ccd..0000000000 --- a/src/sys/socket.rs +++ /dev/null @@ -1,676 +0,0 @@ -use {NixError, NixResult, from_ffi}; -use errno::Errno; -use features; -use fcntl::{fcntl, FD_CLOEXEC, O_NONBLOCK}; -use fcntl::FcntlArg::{F_SETFD, F_SETFL}; -use libc::{c_void, c_int, socklen_t, size_t, ssize_t}; -use std::{fmt, mem, net, ptr, path}; -use std::ffi::{AsOsStr, CStr, OsStr}; -use std::os::unix::prelude::*; - -/* - * - * ===== Re-exports ===== - * - */ - -pub use libc::{ - in_addr, - sockaddr, - sockaddr_storage, - sockaddr_in, - sockaddr_in6, - sockaddr_un, - sa_family_t, - ip_mreq -}; -pub use self::consts::*; - -mod ffi { - use libc::{c_int, c_void, socklen_t}; - pub use libc::{socket, listen, bind, accept, connect, setsockopt, sendto, recvfrom, getsockname, getpeername}; - - extern { - pub fn getsockopt( - sockfd: c_int, - level: c_int, - optname: c_int, - optval: *mut c_void, - optlen: *mut socklen_t) -> c_int; - } -} - -// Extra flags - Supported by Linux 2.6.27, normalized on other platforms -bitflags!( - flags SockFlag: c_int { - const SOCK_NONBLOCK = 0o0004000, - const SOCK_CLOEXEC = 0o2000000 - } -); - -/* - * - * ===== SockAddr ===== - * - */ - -/// Represents a socket address -#[derive(Copy)] -pub enum SockAddr { - // TODO: Rename these variants IpV4, IpV6, Unix - SockIpV4(sockaddr_in), - SockIpV6(sockaddr_in6), - SockUnix(sockaddr_un) -} - -/// A trait for values which can be converted or resolved to a SockAddr. -pub trait ToSockAddr { - /// Converts the value to a SockAddr - fn to_sock_addr(&self) -> NixResult; - - /// Converts and yields the value as a SockAddr - fn with_sock_addr T>(&self, action: F) -> NixResult { - Ok(action(&try!(self.to_sock_addr()))) - } -} - -impl ToSockAddr for SockAddr { - fn to_sock_addr(&self) -> NixResult { - Ok(*self) - } - - fn with_sock_addr T>(&self, action: F) -> NixResult { - Ok(action(self)) - } -} - -/// Convert a path into a unix domain socket address -impl ToSockAddr for path::Path { - fn to_sock_addr(&self) -> NixResult { - let bytes = self.as_os_str().as_bytes(); - - Ok(SockAddr::SockUnix(unsafe { - let mut ret = sockaddr_un { - sun_family: AF_UNIX as sa_family_t, - .. mem::zeroed() - }; - - // Make sure the destination has enough capacity - if bytes.len() >= ret.sun_path.len() { - return Err(NixError::Sys(Errno::ENAMETOOLONG)); - } - - // Copy the path - ptr::copy_memory( - ret.sun_path.as_mut_ptr(), - bytes.as_ptr() as *const i8, - bytes.len()); - - ret - })) - } -} - -/// Convert an inet address into a socket address -impl ToSockAddr for net::SocketAddr { - fn to_sock_addr(&self) -> NixResult { - use std::net::IpAddr; - use std::num::Int; - - match self.ip() { - IpAddr::V4(ip) => { - let addr = ip.octets(); - Ok(SockAddr::SockIpV4(sockaddr_in { - sin_family: AF_INET as sa_family_t, - sin_port: self.port(), - sin_addr: in_addr { - s_addr: Int::from_be( - ((addr[0] as u32) << 24) | - ((addr[1] as u32) << 16) | - ((addr[2] as u32) << 8) | - ((addr[3] as u32) << 0)) - }, - .. unsafe { mem::zeroed() } - })) - } - _ => unimplemented!() - } - } -} - -/// Convert from a socket address -pub trait FromSockAddr { - fn from_sock_addr(addr: &SockAddr) -> Option; -} - -impl FromSockAddr for net::SocketAddr { - fn from_sock_addr(addr: &SockAddr) -> Option { - use std::net::{IpAddr, Ipv4Addr}; - use std::num::Int; - - match *addr { - SockAddr::SockIpV4(ref addr) => { - let ip = Int::from_be(addr.sin_addr.s_addr); - let ip = Ipv4Addr::new( - ((ip >> 24) as u8) & 0xff, - ((ip >> 16) as u8) & 0xff, - ((ip >> 8) as u8) & 0xff, - ((ip >> 0) as u8) & 0xff); - - Some(net::SocketAddr::new(IpAddr::V4(ip), addr.sin_port)) - } - SockAddr::SockIpV6(_) => unimplemented!(), - _ => None, - } - } -} - -impl FromSockAddr for path::PathBuf { - fn from_sock_addr(addr: &SockAddr) -> Option { - if let SockAddr::SockUnix(ref addr) = *addr { - unsafe { - let bytes = CStr::from_ptr(addr.sun_path.as_ptr()).to_bytes(); - let osstr = ::from_bytes(bytes); - return Some(path::PathBuf::new(osstr)); - } - } - - None - } -} - -/* - * - * ===== Consts ===== - * - */ - -#[cfg(target_os = "linux")] -mod consts { - use libc::{c_int, uint8_t}; - - pub type AddressFamily = c_int; - - pub const AF_UNIX: AddressFamily = 1; - pub const AF_LOCAL: AddressFamily = AF_UNIX; - pub const AF_INET: AddressFamily = 2; - pub const AF_INET6: AddressFamily = 10; - - pub type SockType = c_int; - - pub const SOCK_STREAM: SockType = 1; - pub const SOCK_DGRAM: SockType = 2; - pub const SOCK_SEQPACKET: SockType = 5; - pub const SOCK_RAW: SockType = 3; - pub const SOCK_RDM: SockType = 4; - - pub type SockLevel = c_int; - - pub const SOL_IP: SockLevel = 0; - pub const IPPROTO_IP: SockLevel = SOL_IP; - pub const SOL_SOCKET: SockLevel = 1; - pub const SOL_TCP: SockLevel = 6; - pub const IPPROTO_TCP: SockLevel = SOL_TCP; - pub const SOL_UDP: SockLevel = 17; - pub const SOL_IPV6: SockLevel = 41; - - pub type SockOpt = c_int; - - pub const SO_ACCEPTCONN: SockOpt = 30; - pub const SO_BINDTODEVICE: SockOpt = 25; - pub const SO_BROADCAST: SockOpt = 6; - pub const SO_BSDCOMPAT: SockOpt = 14; - pub const SO_DEBUG: SockOpt = 1; - pub const SO_DOMAIN: SockOpt = 39; - pub const SO_ERROR: SockOpt = 4; - pub const SO_DONTROUTE: SockOpt = 5; - pub const SO_KEEPALIVE: SockOpt = 9; - pub const SO_LINGER: SockOpt = 13; - pub const SO_MARK: SockOpt = 36; - pub const SO_OOBINLINE: SockOpt = 10; - pub const SO_PASSCRED: SockOpt = 16; - pub const SO_PEEK_OFF: SockOpt = 42; - pub const SO_PEERCRED: SockOpt = 17; - pub const SO_PRIORITY: SockOpt = 12; - pub const SO_PROTOCOL: SockOpt = 38; - pub const SO_RCVBUF: SockOpt = 8; - pub const SO_RCVBUFFORCE: SockOpt = 33; - pub const SO_RCVLOWAT: SockOpt = 18; - pub const SO_SNDLOWAT: SockOpt = 19; - pub const SO_RCVTIMEO: SockOpt = 20; - pub const SO_SNDTIMEO: SockOpt = 21; - pub const SO_REUSEADDR: SockOpt = 2; - pub const SO_REUSEPORT: SockOpt = 15; - pub const SO_RXQ_OVFL: SockOpt = 40; - pub const SO_SNDBUF: SockOpt = 7; - pub const SO_SNDBUFFORCE: SockOpt = 32; - pub const SO_TIMESTAMP: SockOpt = 29; - pub const SO_TYPE: SockOpt = 3; - pub const SO_BUSY_POLL: SockOpt = 46; - - // Socket options for TCP sockets - pub const TCP_NODELAY: SockOpt = 1; - pub const TCP_MAXSEG: SockOpt = 2; - pub const TCP_CORK: SockOpt = 3; - - // Socket options for the IP layer of the socket - pub const IP_MULTICAST_IF: SockOpt = 32; - - pub type IpMulticastTtl = uint8_t; - - pub const IP_MULTICAST_TTL: SockOpt = 33; - pub const IP_MULTICAST_LOOP: SockOpt = 34; - pub const IP_ADD_MEMBERSHIP: SockOpt = 35; - pub const IP_DROP_MEMBERSHIP: SockOpt = 36; - - pub type InAddrT = u32; - - // Declarations of special addresses - pub const INADDR_ANY: InAddrT = 0; - pub const INADDR_NONE: InAddrT = 0xffffffff; - pub const INADDR_BROADCAST: InAddrT = 0xffffffff; - - pub type SockMessageFlags = i32; - // Flags for send/recv and their relatives - pub const MSG_OOB: SockMessageFlags = 0x1; - pub const MSG_PEEK: SockMessageFlags = 0x2; - pub const MSG_DONTWAIT: SockMessageFlags = 0x40; -} - -#[cfg(any(target_os = "macos", target_os = "ios"))] -mod consts { - use libc::{c_int, uint8_t}; - - pub type AddressFamily = c_int; - - pub const AF_UNIX: AddressFamily = 1; - pub const AF_LOCAL: AddressFamily = AF_UNIX; - pub const AF_INET: AddressFamily = 2; - pub const AF_INET6: AddressFamily = 30; - - pub type SockType = c_int; - - pub const SOCK_STREAM: SockType = 1; - pub const SOCK_DGRAM: SockType = 2; - pub const SOCK_SEQPACKET: SockType = 5; - pub const SOCK_RAW: SockType = 3; - pub const SOCK_RDM: SockType = 4; - - pub type SockLevel = c_int; - - pub const SOL_SOCKET: SockLevel = 0xffff; - pub const IPPROTO_IP: SockLevel = 0; - pub const IPPROTO_TCP: SockLevel = 6; - pub const IPPROTO_UDP: SockLevel = 17; - - pub type SockOpt = c_int; - - pub const SO_ACCEPTCONN: SockOpt = 0x0002; - pub const SO_BROADCAST: SockOpt = 0x0020; - pub const SO_DEBUG: SockOpt = 0x0001; - pub const SO_DONTTRUNC: SockOpt = 0x2000; - pub const SO_ERROR: SockOpt = 0x1007; - pub const SO_DONTROUTE: SockOpt = 0x0010; - pub const SO_KEEPALIVE: SockOpt = 0x0008; - pub const SO_LABEL: SockOpt = 0x1010; - pub const SO_LINGER: SockOpt = 0x0080; - pub const SO_NREAD: SockOpt = 0x1020; - pub const SO_NKE: SockOpt = 0x1021; - pub const SO_NOSIGPIPE: SockOpt = 0x1022; - pub const SO_NOADDRERR: SockOpt = 0x1023; - pub const SO_NOTIFYCONFLICT: SockOpt = 0x1026; - pub const SO_NP_EXTENSIONS: SockOpt = 0x1083; - pub const SO_NWRITE: SockOpt = 0x1024; - pub const SO_OOBINLINE: SockOpt = 0x0100; - pub const SO_PEERLABEL: SockOpt = 0x1011; - pub const SO_RCVBUF: SockOpt = 0x1002; - pub const SO_RCVLOWAT: SockOpt = 0x1004; - pub const SO_SNDLOWAT: SockOpt = 0x1003; - pub const SO_RCVTIMEO: SockOpt = 0x1006; - pub const SO_SNDTIMEO: SockOpt = 0x1005; - pub const SO_RANDOMPORT: SockOpt = 0x1082; - pub const SO_RESTRICTIONS: SockOpt = 0x1081; - pub const SO_RESTRICT_DENYIN: SockOpt = 0x00000001; - pub const SO_RESTRICT_DENYOUT: SockOpt = 0x00000002; - pub const SO_REUSEADDR: SockOpt = 0x0004; - pub const SO_REUSEPORT: SockOpt = 0x0200; - pub const SO_REUSESHAREUID: SockOpt = 0x1025; - pub const SO_SNDBUF: SockOpt = 0x1001; - pub const SO_TIMESTAMP: SockOpt = 0x0400; - pub const SO_TIMESTAMP_MONOTONIC: SockOpt = 0x0800; - pub const SO_TYPE: SockOpt = 0x1008; - pub const SO_WANTMORE: SockOpt = 0x4000; - pub const SO_WANTOOBFLAG: SockOpt = 0x8000; - #[allow(overflowing_literals)] - pub const SO_RESTRICT_DENYSET: SockOpt = 0x80000000; - - // Socket options for TCP sockets - pub const TCP_NODELAY: SockOpt = 1; - pub const TCP_MAXSEG: SockOpt = 2; - - // Socket options for the IP layer of the socket - pub const IP_MULTICAST_IF: SockOpt = 9; - - pub type IpMulticastTtl = uint8_t; - - pub const IP_MULTICAST_TTL: SockOpt = 10; - pub const IP_MULTICAST_LOOP: SockOpt = 11; - pub const IP_ADD_MEMBERSHIP: SockOpt = 12; - pub const IP_DROP_MEMBERSHIP: SockOpt = 13; - - pub type InAddrT = u32; - - // Declarations of special addresses - pub const INADDR_ANY: InAddrT = 0; - pub const INADDR_NONE: InAddrT = 0xffffffff; - pub const INADDR_BROADCAST: InAddrT = 0xffffffff; - - pub type SockMessageFlags = i32; - // Flags for send/recv and their relatives - pub const MSG_OOB: SockMessageFlags = 0x1; - pub const MSG_PEEK: SockMessageFlags = 0x2; - pub const MSG_DONTWAIT: SockMessageFlags = 0x80; -} - -pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> NixResult { - let feat_atomic = features::socket_atomic_cloexec(); - - if feat_atomic { - ty = ty | flags.bits(); - } - - // TODO: Check the kernel version - let res = unsafe { ffi::socket(domain, ty, 0) }; - - if res < 0 { - return Err(NixError::Sys(Errno::last())); - } - - if !feat_atomic { - if flags.contains(SOCK_CLOEXEC) { - try!(fcntl(res, F_SETFD(FD_CLOEXEC))); - } - - if flags.contains(SOCK_NONBLOCK) { - try!(fcntl(res, F_SETFL(O_NONBLOCK))); - } - } - - Ok(res) -} - -pub fn listen(sockfd: Fd, backlog: usize) -> NixResult<()> { - let res = unsafe { ffi::listen(sockfd, backlog as c_int) }; - from_ffi(res) -} - -pub fn bind(sockfd: Fd, addr: &A) -> NixResult<()> { - use self::SockAddr::*; - - let res = unsafe { - try!(addr.with_sock_addr(|addr| { - match *addr { - SockIpV4(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t), - SockIpV6(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t), - SockUnix(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t) - } - })) - }; - - from_ffi(res) -} - -pub fn accept(sockfd: Fd) -> NixResult { - let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) }; - - if res < 0 { - return Err(NixError::Sys(Errno::last())); - } - - Ok(res) -} - -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult { - use libc::sockaddr; - - type F = unsafe extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int; - - extern { - #[linkage = "extern_weak"] - static accept4: *const (); - } - - if !accept4.is_null() { - let res = unsafe { - mem::transmute::<*const (), F>(accept4)( - sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits) - }; - - if res < 0 { - return Err(NixError::Sys(Errno::last())); - } - - Ok(res) - } else { - accept4_polyfill(sockfd, flags) - } -} - -#[cfg(any(target_os = "macos", target_os = "ios"))] -pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult { - accept4_polyfill(sockfd, flags) -} - -#[inline] -fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> NixResult { - let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) }; - - if res < 0 { - return Err(NixError::Sys(Errno::last())); - } - - if flags.contains(SOCK_CLOEXEC) { - try!(fcntl(res, F_SETFD(FD_CLOEXEC))); - } - - if flags.contains(SOCK_NONBLOCK) { - try!(fcntl(res, F_SETFL(O_NONBLOCK))); - } - - Ok(res) -} - -pub fn connect(sockfd: Fd, addr: &A) -> NixResult<()> { - use self::SockAddr::*; - - let res = unsafe { - try!(addr.with_sock_addr(|addr| { - match *addr { - SockIpV4(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t), - SockIpV6(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t), - SockUnix(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t) - } - })) - }; - - from_ffi(res) -} - -mod sa_helpers { - use std::mem; - use libc::{sockaddr_storage, sockaddr_in, sockaddr_in6, sockaddr_un}; - use super::SockAddr; - use super::SockAddr::*; - - pub fn to_sockaddr_ipv4(addr: &sockaddr_storage) -> SockAddr { - let sin : &sockaddr_in = unsafe { mem::transmute(addr) }; - SockIpV4( *sin ) - } - - pub fn to_sockaddr_ipv6(addr: &sockaddr_storage) -> SockAddr { - let sin6 : &sockaddr_in6 = unsafe { mem::transmute(addr) }; - SockIpV6( *sin6 ) - } - - pub fn to_sockaddr_unix(addr: &sockaddr_storage) -> SockAddr { - let sun : &sockaddr_un = unsafe { mem::transmute(addr) }; - SockUnix( *sun ) - } -} - -pub fn recvfrom(sockfd: Fd, buf: &mut [u8]) -> NixResult<(usize, SockAddr)> { - let saddr : sockaddr_storage = unsafe { mem::zeroed() }; - let mut len = mem::size_of::() as socklen_t; - - let ret = unsafe { - ffi::recvfrom(sockfd, buf.as_ptr() as *mut c_void, buf.len() as size_t, 0, mem::transmute(&saddr), &mut len as *mut socklen_t) - }; - - if ret < 0 { - return Err(NixError::Sys(Errno::last())); - } - - Ok((ret as usize, - match saddr.ss_family as i32 { - AF_INET => { sa_helpers::to_sockaddr_ipv4(&saddr) }, - AF_INET6 => { sa_helpers::to_sockaddr_ipv6(&saddr) }, - AF_UNIX => { sa_helpers::to_sockaddr_unix(&saddr) }, - _ => unimplemented!() - } - )) -} - -fn print_ipv4_addr(sin: &sockaddr_in, f: &mut fmt::Formatter) -> fmt::Result { - use std::num::Int; - - let ip_addr = Int::from_be(sin.sin_addr.s_addr); - let port = Int::from_be(sin.sin_port); - - write!(f, "{}.{}.{}.{}:{}", - (ip_addr >> 24) & 0xff, - (ip_addr >> 16) & 0xff, - (ip_addr >> 8) & 0xff, - (ip_addr) & 0xff, - port) -} - -impl fmt::Debug for SockAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - SockAddr::SockIpV4(sin) => print_ipv4_addr(&sin, f), - _ => unimplemented!() - } - } -} - -/// -/// Generic wrapper around sendto -fn sendto_sockaddr(sockfd: Fd, buf: &[u8], flags: SockMessageFlags, addr: &T) -> ssize_t { - unsafe { - ffi::sendto( - sockfd, - buf.as_ptr() as *const c_void, - buf.len() as size_t, - flags, - mem::transmute(addr), - mem::size_of::() as socklen_t) - } -} - -pub fn sendto(sockfd: Fd, buf: &[u8], addr: &SockAddr, flags: SockMessageFlags) -> NixResult { - use self::SockAddr::*; - - let ret = match *addr { - SockIpV4(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), - SockIpV6(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), - SockUnix(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), - }; - - if ret < 0 { - Err(NixError::Sys(Errno::last())) - } else { - Ok(ret as usize) - } -} - -#[repr(C)] -#[derive(Copy)] -pub struct linger { - pub l_onoff: c_int, - pub l_linger: c_int -} - -pub fn getsockopt(fd: Fd, level: SockLevel, opt: SockOpt, val: &mut T) -> NixResult { - let mut len = mem::size_of::() as socklen_t; - - let res = unsafe { - ffi::getsockopt( - fd, level, opt, - mem::transmute(val), - &mut len as *mut socklen_t) - }; - - if res < 0 { - return Err(NixError::Sys(Errno::last())); - } - - Ok(len as usize) -} - -pub fn setsockopt(fd: Fd, level: SockLevel, opt: SockOpt, val: &T) -> NixResult<()> { - let len = mem::size_of::() as socklen_t; - - let res = unsafe { - ffi::setsockopt( - fd, level, opt, - mem::transmute(val), - len) - }; - - from_ffi(res) -} - -fn getpeername_sockaddr(sockfd: Fd, addr: &T) -> NixResult { - let addrlen_expected = mem::size_of::() as socklen_t; - let mut addrlen = addrlen_expected; - - let ret = unsafe { ffi::getpeername(sockfd, mem::transmute(addr), &mut addrlen) }; - if ret < 0 { - return Err(NixError::Sys(Errno::last())); - } - - Ok(addrlen == addrlen_expected) -} - -pub fn getpeername(sockfd: Fd, addr: &mut SockAddr) -> NixResult { - use self::SockAddr::*; - - match *addr { - SockIpV4(ref addr) => getpeername_sockaddr(sockfd, addr), - SockIpV6(ref addr) => getpeername_sockaddr(sockfd, addr), - SockUnix(ref addr) => getpeername_sockaddr(sockfd, addr) - } -} - -fn getsockname_sockaddr(sockfd: Fd, addr: &T) -> NixResult { - let addrlen_expected = mem::size_of::() as socklen_t; - let mut addrlen = addrlen_expected; - - let ret = unsafe { ffi::getsockname(sockfd, mem::transmute(addr), &mut addrlen) }; - if ret < 0 { - return Err(NixError::Sys(Errno::last())); - } - - Ok(addrlen == addrlen_expected) -} - -pub fn getsockname(sockfd: Fd, addr: &mut SockAddr) -> NixResult { - use self::SockAddr::*; - - match *addr { - SockIpV4(ref addr) => getsockname_sockaddr(sockfd, addr), - SockIpV6(ref addr) => getsockname_sockaddr(sockfd, addr), - SockUnix(ref addr) => getsockname_sockaddr(sockfd, addr) - } -} diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs new file mode 100644 index 0000000000..91d2c0d15d --- /dev/null +++ b/src/sys/socket/addr.rs @@ -0,0 +1,131 @@ +use {NixResult, NixError}; +use super::{sa_family_t, in_addr, sockaddr_in, sockaddr_in6, sockaddr_un, AF_UNIX, AF_INET}; +use errno::Errno; +use std::{mem, net, path, ptr}; +use std::ffi::{AsOsStr, CStr, OsStr}; +use std::os::unix::OsStrExt; + +/// Represents a socket address +#[derive(Copy)] +pub enum SockAddr { + // TODO: Rename these variants IpV4, IpV6, Unix + SockIpV4(sockaddr_in), + SockIpV6(sockaddr_in6), + SockUnix(sockaddr_un) +} + +/// A trait for values which can be converted or resolved to a SockAddr. +pub trait ToSockAddr { + /// Converts the value to a SockAddr + fn to_sock_addr(&self) -> NixResult; + + /// Converts and yields the value as a SockAddr + fn with_sock_addr T>(&self, action: F) -> NixResult { + Ok(action(&try!(self.to_sock_addr()))) + } +} + +impl ToSockAddr for SockAddr { + fn to_sock_addr(&self) -> NixResult { + Ok(*self) + } + + fn with_sock_addr T>(&self, action: F) -> NixResult { + Ok(action(self)) + } +} + +/// Convert a path into a unix domain socket address +impl ToSockAddr for path::Path { + fn to_sock_addr(&self) -> NixResult { + let bytes = self.as_os_str().as_bytes(); + + Ok(SockAddr::SockUnix(unsafe { + let mut ret = sockaddr_un { + sun_family: AF_UNIX as sa_family_t, + .. mem::zeroed() + }; + + // Make sure the destination has enough capacity + if bytes.len() >= ret.sun_path.len() { + return Err(NixError::Sys(Errno::ENAMETOOLONG)); + } + + // Copy the path + ptr::copy_memory( + ret.sun_path.as_mut_ptr(), + bytes.as_ptr() as *const i8, + bytes.len()); + + ret + })) + } +} + +/// Convert an inet address into a socket address +impl ToSockAddr for net::SocketAddr { + fn to_sock_addr(&self) -> NixResult { + use std::net::IpAddr; + use std::num::Int; + + match self.ip() { + IpAddr::V4(ip) => { + let addr = ip.octets(); + Ok(SockAddr::SockIpV4(sockaddr_in { + sin_family: AF_INET as sa_family_t, + sin_port: self.port(), + sin_addr: in_addr { + s_addr: Int::from_be( + ((addr[0] as u32) << 24) | + ((addr[1] as u32) << 16) | + ((addr[2] as u32) << 8) | + ((addr[3] as u32) << 0)) + }, + .. unsafe { mem::zeroed() } + })) + } + _ => unimplemented!() + } + } +} + +/// Convert from a socket address +pub trait FromSockAddr { + fn from_sock_addr(addr: &SockAddr) -> Option; +} + +impl FromSockAddr for net::SocketAddr { + fn from_sock_addr(addr: &SockAddr) -> Option { + use std::net::{IpAddr, Ipv4Addr}; + use std::num::Int; + + match *addr { + SockAddr::SockIpV4(ref addr) => { + let ip = Int::from_be(addr.sin_addr.s_addr); + let ip = Ipv4Addr::new( + ((ip >> 24) as u8) & 0xff, + ((ip >> 16) as u8) & 0xff, + ((ip >> 8) as u8) & 0xff, + ((ip >> 0) as u8) & 0xff); + + Some(net::SocketAddr::new(IpAddr::V4(ip), addr.sin_port)) + } + SockAddr::SockIpV6(_) => unimplemented!(), + _ => None, + } + } +} + +impl FromSockAddr for path::PathBuf { + fn from_sock_addr(addr: &SockAddr) -> Option { + if let SockAddr::SockUnix(ref addr) = *addr { + unsafe { + let bytes = CStr::from_ptr(addr.sun_path.as_ptr()).to_bytes(); + let osstr = ::from_bytes(bytes); + return Some(path::PathBuf::new(osstr)); + } + } + + None + } +} diff --git a/src/sys/socket/consts.rs b/src/sys/socket/consts.rs new file mode 100644 index 0000000000..e23098b646 --- /dev/null +++ b/src/sys/socket/consts.rs @@ -0,0 +1,188 @@ +pub use self::os::*; + +#[cfg(target_os = "linux")] +mod os { + use libc::{c_int, uint8_t}; + + pub type AddressFamily = c_int; + + pub const AF_UNIX: AddressFamily = 1; + pub const AF_LOCAL: AddressFamily = AF_UNIX; + pub const AF_INET: AddressFamily = 2; + pub const AF_INET6: AddressFamily = 10; + + pub type SockType = c_int; + + pub const SOCK_STREAM: SockType = 1; + pub const SOCK_DGRAM: SockType = 2; + pub const SOCK_SEQPACKET: SockType = 5; + pub const SOCK_RAW: SockType = 3; + pub const SOCK_RDM: SockType = 4; + + pub type SockLevel = c_int; + + pub const SOL_IP: SockLevel = 0; + pub const IPPROTO_IP: SockLevel = SOL_IP; + pub const SOL_SOCKET: SockLevel = 1; + pub const SOL_TCP: SockLevel = 6; + pub const IPPROTO_TCP: SockLevel = SOL_TCP; + pub const SOL_UDP: SockLevel = 17; + pub const SOL_IPV6: SockLevel = 41; + + pub type SockOpt = c_int; + + pub const SO_ACCEPTCONN: SockOpt = 30; + pub const SO_BINDTODEVICE: SockOpt = 25; + pub const SO_BROADCAST: SockOpt = 6; + pub const SO_BSDCOMPAT: SockOpt = 14; + pub const SO_DEBUG: SockOpt = 1; + pub const SO_DOMAIN: SockOpt = 39; + pub const SO_ERROR: SockOpt = 4; + pub const SO_DONTROUTE: SockOpt = 5; + pub const SO_KEEPALIVE: SockOpt = 9; + pub const SO_LINGER: SockOpt = 13; + pub const SO_MARK: SockOpt = 36; + pub const SO_OOBINLINE: SockOpt = 10; + pub const SO_PASSCRED: SockOpt = 16; + pub const SO_PEEK_OFF: SockOpt = 42; + pub const SO_PEERCRED: SockOpt = 17; + pub const SO_PRIORITY: SockOpt = 12; + pub const SO_PROTOCOL: SockOpt = 38; + pub const SO_RCVBUF: SockOpt = 8; + pub const SO_RCVBUFFORCE: SockOpt = 33; + pub const SO_RCVLOWAT: SockOpt = 18; + pub const SO_SNDLOWAT: SockOpt = 19; + pub const SO_RCVTIMEO: SockOpt = 20; + pub const SO_SNDTIMEO: SockOpt = 21; + pub const SO_REUSEADDR: SockOpt = 2; + pub const SO_REUSEPORT: SockOpt = 15; + pub const SO_RXQ_OVFL: SockOpt = 40; + pub const SO_SNDBUF: SockOpt = 7; + pub const SO_SNDBUFFORCE: SockOpt = 32; + pub const SO_TIMESTAMP: SockOpt = 29; + pub const SO_TYPE: SockOpt = 3; + pub const SO_BUSY_POLL: SockOpt = 46; + + // Socket options for TCP sockets + pub const TCP_NODELAY: SockOpt = 1; + pub const TCP_MAXSEG: SockOpt = 2; + pub const TCP_CORK: SockOpt = 3; + + // Socket options for the IP layer of the socket + pub const IP_MULTICAST_IF: SockOpt = 32; + + pub type IpMulticastTtl = uint8_t; + + pub const IP_MULTICAST_TTL: SockOpt = 33; + pub const IP_MULTICAST_LOOP: SockOpt = 34; + pub const IP_ADD_MEMBERSHIP: SockOpt = 35; + pub const IP_DROP_MEMBERSHIP: SockOpt = 36; + + pub type InAddrT = u32; + + // Declarations of special addresses + pub const INADDR_ANY: InAddrT = 0; + pub const INADDR_NONE: InAddrT = 0xffffffff; + pub const INADDR_BROADCAST: InAddrT = 0xffffffff; + + pub type SockMessageFlags = i32; + // Flags for send/recv and their relatives + pub const MSG_OOB: SockMessageFlags = 0x1; + pub const MSG_PEEK: SockMessageFlags = 0x2; + pub const MSG_DONTWAIT: SockMessageFlags = 0x40; +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod os { + use libc::{c_int, uint8_t}; + + pub type AddressFamily = c_int; + + pub const AF_UNIX: AddressFamily = 1; + pub const AF_LOCAL: AddressFamily = AF_UNIX; + pub const AF_INET: AddressFamily = 2; + pub const AF_INET6: AddressFamily = 30; + + pub type SockType = c_int; + + pub const SOCK_STREAM: SockType = 1; + pub const SOCK_DGRAM: SockType = 2; + pub const SOCK_SEQPACKET: SockType = 5; + pub const SOCK_RAW: SockType = 3; + pub const SOCK_RDM: SockType = 4; + + pub type SockLevel = c_int; + + pub const SOL_SOCKET: SockLevel = 0xffff; + pub const IPPROTO_IP: SockLevel = 0; + pub const IPPROTO_TCP: SockLevel = 6; + pub const IPPROTO_UDP: SockLevel = 17; + + pub type SockOpt = c_int; + + pub const SO_ACCEPTCONN: SockOpt = 0x0002; + pub const SO_BROADCAST: SockOpt = 0x0020; + pub const SO_DEBUG: SockOpt = 0x0001; + pub const SO_DONTTRUNC: SockOpt = 0x2000; + pub const SO_ERROR: SockOpt = 0x1007; + pub const SO_DONTROUTE: SockOpt = 0x0010; + pub const SO_KEEPALIVE: SockOpt = 0x0008; + pub const SO_LABEL: SockOpt = 0x1010; + pub const SO_LINGER: SockOpt = 0x0080; + pub const SO_NREAD: SockOpt = 0x1020; + pub const SO_NKE: SockOpt = 0x1021; + pub const SO_NOSIGPIPE: SockOpt = 0x1022; + pub const SO_NOADDRERR: SockOpt = 0x1023; + pub const SO_NOTIFYCONFLICT: SockOpt = 0x1026; + pub const SO_NP_EXTENSIONS: SockOpt = 0x1083; + pub const SO_NWRITE: SockOpt = 0x1024; + pub const SO_OOBINLINE: SockOpt = 0x0100; + pub const SO_PEERLABEL: SockOpt = 0x1011; + pub const SO_RCVBUF: SockOpt = 0x1002; + pub const SO_RCVLOWAT: SockOpt = 0x1004; + pub const SO_SNDLOWAT: SockOpt = 0x1003; + pub const SO_RCVTIMEO: SockOpt = 0x1006; + pub const SO_SNDTIMEO: SockOpt = 0x1005; + pub const SO_RANDOMPORT: SockOpt = 0x1082; + pub const SO_RESTRICTIONS: SockOpt = 0x1081; + pub const SO_RESTRICT_DENYIN: SockOpt = 0x00000001; + pub const SO_RESTRICT_DENYOUT: SockOpt = 0x00000002; + pub const SO_REUSEADDR: SockOpt = 0x0004; + pub const SO_REUSEPORT: SockOpt = 0x0200; + pub const SO_REUSESHAREUID: SockOpt = 0x1025; + pub const SO_SNDBUF: SockOpt = 0x1001; + pub const SO_TIMESTAMP: SockOpt = 0x0400; + pub const SO_TIMESTAMP_MONOTONIC: SockOpt = 0x0800; + pub const SO_TYPE: SockOpt = 0x1008; + pub const SO_WANTMORE: SockOpt = 0x4000; + pub const SO_WANTOOBFLAG: SockOpt = 0x8000; + #[allow(overflowing_literals)] + pub const SO_RESTRICT_DENYSET: SockOpt = 0x80000000; + + // Socket options for TCP sockets + pub const TCP_NODELAY: SockOpt = 1; + pub const TCP_MAXSEG: SockOpt = 2; + + // Socket options for the IP layer of the socket + pub const IP_MULTICAST_IF: SockOpt = 9; + + pub type IpMulticastTtl = uint8_t; + + pub const IP_MULTICAST_TTL: SockOpt = 10; + pub const IP_MULTICAST_LOOP: SockOpt = 11; + pub const IP_ADD_MEMBERSHIP: SockOpt = 12; + pub const IP_DROP_MEMBERSHIP: SockOpt = 13; + + pub type InAddrT = u32; + + // Declarations of special addresses + pub const INADDR_ANY: InAddrT = 0; + pub const INADDR_NONE: InAddrT = 0xffffffff; + pub const INADDR_BROADCAST: InAddrT = 0xffffffff; + + pub type SockMessageFlags = i32; + // Flags for send/recv and their relatives + pub const MSG_OOB: SockMessageFlags = 0x1; + pub const MSG_PEEK: SockMessageFlags = 0x2; + pub const MSG_DONTWAIT: SockMessageFlags = 0x80; +} diff --git a/src/sys/socket/ffi.rs b/src/sys/socket/ffi.rs new file mode 100644 index 0000000000..612854de7d --- /dev/null +++ b/src/sys/socket/ffi.rs @@ -0,0 +1,11 @@ +use libc::{c_int, c_void, socklen_t}; +pub use libc::{socket, listen, bind, accept, connect, setsockopt, sendto, recvfrom, getsockname, getpeername}; + +extern { + pub fn getsockopt( + sockfd: c_int, + level: c_int, + optname: c_int, + optval: *mut c_void, + optlen: *mut socklen_t) -> c_int; +} diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs new file mode 100644 index 0000000000..a5c2c97ae0 --- /dev/null +++ b/src/sys/socket/mod.rs @@ -0,0 +1,346 @@ +use {NixError, NixResult, from_ffi}; +use errno::Errno; +use features; +use fcntl::{fcntl, FD_CLOEXEC, O_NONBLOCK}; +use fcntl::FcntlArg::{F_SETFD, F_SETFL}; +use libc::{c_void, c_int, socklen_t, size_t, ssize_t}; +use std::{fmt, mem, ptr}; +use std::os::unix::prelude::*; + +mod addr; +mod consts; +mod ffi; + +/* + * + * ===== Re-exports ===== + * + */ + +pub use self::addr::{ + SockAddr, + ToSockAddr, + FromSockAddr +}; +pub use libc::{ + in_addr, + sockaddr, + sockaddr_storage, + sockaddr_in, + sockaddr_in6, + sockaddr_un, + sa_family_t, + ip_mreq +}; +pub use self::consts::*; + +// Extra flags - Supported by Linux 2.6.27, normalized on other platforms +bitflags!( + flags SockFlag: c_int { + const SOCK_NONBLOCK = 0o0004000, + const SOCK_CLOEXEC = 0o2000000 + } +); + +pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> NixResult { + let feat_atomic = features::socket_atomic_cloexec(); + + if feat_atomic { + ty = ty | flags.bits(); + } + + // TODO: Check the kernel version + let res = unsafe { ffi::socket(domain, ty, 0) }; + + if res < 0 { + return Err(NixError::Sys(Errno::last())); + } + + if !feat_atomic { + if flags.contains(SOCK_CLOEXEC) { + try!(fcntl(res, F_SETFD(FD_CLOEXEC))); + } + + if flags.contains(SOCK_NONBLOCK) { + try!(fcntl(res, F_SETFL(O_NONBLOCK))); + } + } + + Ok(res) +} + +pub fn listen(sockfd: Fd, backlog: usize) -> NixResult<()> { + let res = unsafe { ffi::listen(sockfd, backlog as c_int) }; + from_ffi(res) +} + +pub fn bind(sockfd: Fd, addr: &A) -> NixResult<()> { + use self::addr::SockAddr::*; + + let res = unsafe { + try!(addr.with_sock_addr(|addr| { + match *addr { + SockIpV4(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t), + SockIpV6(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t), + SockUnix(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t) + } + })) + }; + + from_ffi(res) +} + +pub fn accept(sockfd: Fd) -> NixResult { + let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) }; + + if res < 0 { + return Err(NixError::Sys(Errno::last())); + } + + Ok(res) +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult { + use libc::sockaddr; + + type F = unsafe extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int; + + extern { + #[linkage = "extern_weak"] + static accept4: *const (); + } + + if !accept4.is_null() { + let res = unsafe { + mem::transmute::<*const (), F>(accept4)( + sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits) + }; + + if res < 0 { + return Err(NixError::Sys(Errno::last())); + } + + Ok(res) + } else { + accept4_polyfill(sockfd, flags) + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult { + accept4_polyfill(sockfd, flags) +} + +#[inline] +fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> NixResult { + let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) }; + + if res < 0 { + return Err(NixError::Sys(Errno::last())); + } + + if flags.contains(SOCK_CLOEXEC) { + try!(fcntl(res, F_SETFD(FD_CLOEXEC))); + } + + if flags.contains(SOCK_NONBLOCK) { + try!(fcntl(res, F_SETFL(O_NONBLOCK))); + } + + Ok(res) +} + +pub fn connect(sockfd: Fd, addr: &A) -> NixResult<()> { + use self::addr::SockAddr::*; + + let res = unsafe { + try!(addr.with_sock_addr(|addr| { + match *addr { + SockIpV4(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t), + SockIpV6(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t), + SockUnix(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::() as socklen_t) + } + })) + }; + + from_ffi(res) +} + +mod sa_helpers { + use std::mem; + use libc::{sockaddr_storage, sockaddr_in, sockaddr_in6, sockaddr_un}; + use super::SockAddr; + use super::SockAddr::*; + + pub fn to_sockaddr_ipv4(addr: &sockaddr_storage) -> SockAddr { + let sin : &sockaddr_in = unsafe { mem::transmute(addr) }; + SockIpV4( *sin ) + } + + pub fn to_sockaddr_ipv6(addr: &sockaddr_storage) -> SockAddr { + let sin6 : &sockaddr_in6 = unsafe { mem::transmute(addr) }; + SockIpV6( *sin6 ) + } + + pub fn to_sockaddr_unix(addr: &sockaddr_storage) -> SockAddr { + let sun : &sockaddr_un = unsafe { mem::transmute(addr) }; + SockUnix( *sun ) + } +} + +pub fn recvfrom(sockfd: Fd, buf: &mut [u8]) -> NixResult<(usize, SockAddr)> { + let saddr : sockaddr_storage = unsafe { mem::zeroed() }; + let mut len = mem::size_of::() as socklen_t; + + let ret = unsafe { + ffi::recvfrom(sockfd, buf.as_ptr() as *mut c_void, buf.len() as size_t, 0, mem::transmute(&saddr), &mut len as *mut socklen_t) + }; + + if ret < 0 { + return Err(NixError::Sys(Errno::last())); + } + + Ok((ret as usize, + match saddr.ss_family as i32 { + AF_INET => { sa_helpers::to_sockaddr_ipv4(&saddr) }, + AF_INET6 => { sa_helpers::to_sockaddr_ipv6(&saddr) }, + AF_UNIX => { sa_helpers::to_sockaddr_unix(&saddr) }, + _ => unimplemented!() + } + )) +} + +fn print_ipv4_addr(sin: &sockaddr_in, f: &mut fmt::Formatter) -> fmt::Result { + use std::num::Int; + + let ip_addr = Int::from_be(sin.sin_addr.s_addr); + let port = Int::from_be(sin.sin_port); + + write!(f, "{}.{}.{}.{}:{}", + (ip_addr >> 24) & 0xff, + (ip_addr >> 16) & 0xff, + (ip_addr >> 8) & 0xff, + (ip_addr) & 0xff, + port) +} + +impl fmt::Debug for SockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SockAddr::SockIpV4(sin) => print_ipv4_addr(&sin, f), + _ => unimplemented!() + } + } +} + +/// +/// Generic wrapper around sendto +fn sendto_sockaddr(sockfd: Fd, buf: &[u8], flags: SockMessageFlags, addr: &T) -> ssize_t { + unsafe { + ffi::sendto( + sockfd, + buf.as_ptr() as *const c_void, + buf.len() as size_t, + flags, + mem::transmute(addr), + mem::size_of::() as socklen_t) + } +} + +pub fn sendto(sockfd: Fd, buf: &[u8], addr: &SockAddr, flags: SockMessageFlags) -> NixResult { + use self::addr::SockAddr::*; + + let ret = match *addr { + SockIpV4(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), + SockIpV6(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), + SockUnix(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), + }; + + if ret < 0 { + Err(NixError::Sys(Errno::last())) + } else { + Ok(ret as usize) + } +} + +#[repr(C)] +#[derive(Copy)] +pub struct linger { + pub l_onoff: c_int, + pub l_linger: c_int +} + +pub fn getsockopt(fd: Fd, level: SockLevel, opt: SockOpt, val: &mut T) -> NixResult { + let mut len = mem::size_of::() as socklen_t; + + let res = unsafe { + ffi::getsockopt( + fd, level, opt, + mem::transmute(val), + &mut len as *mut socklen_t) + }; + + if res < 0 { + return Err(NixError::Sys(Errno::last())); + } + + Ok(len as usize) +} + +pub fn setsockopt(fd: Fd, level: SockLevel, opt: SockOpt, val: &T) -> NixResult<()> { + let len = mem::size_of::() as socklen_t; + + let res = unsafe { + ffi::setsockopt( + fd, level, opt, + mem::transmute(val), + len) + }; + + from_ffi(res) +} + +fn getpeername_sockaddr(sockfd: Fd, addr: &T) -> NixResult { + let addrlen_expected = mem::size_of::() as socklen_t; + let mut addrlen = addrlen_expected; + + let ret = unsafe { ffi::getpeername(sockfd, mem::transmute(addr), &mut addrlen) }; + if ret < 0 { + return Err(NixError::Sys(Errno::last())); + } + + Ok(addrlen == addrlen_expected) +} + +pub fn getpeername(sockfd: Fd, addr: &mut SockAddr) -> NixResult { + use self::addr::SockAddr::*; + + match *addr { + SockIpV4(ref addr) => getpeername_sockaddr(sockfd, addr), + SockIpV6(ref addr) => getpeername_sockaddr(sockfd, addr), + SockUnix(ref addr) => getpeername_sockaddr(sockfd, addr) + } +} + +fn getsockname_sockaddr(sockfd: Fd, addr: &T) -> NixResult { + let addrlen_expected = mem::size_of::() as socklen_t; + let mut addrlen = addrlen_expected; + + let ret = unsafe { ffi::getsockname(sockfd, mem::transmute(addr), &mut addrlen) }; + if ret < 0 { + return Err(NixError::Sys(Errno::last())); + } + + Ok(addrlen == addrlen_expected) +} + +pub fn getsockname(sockfd: Fd, addr: &mut SockAddr) -> NixResult { + use self::addr::SockAddr::*; + + match *addr { + SockIpV4(ref addr) => getsockname_sockaddr(sockfd, addr), + SockIpV6(ref addr) => getsockname_sockaddr(sockfd, addr), + SockUnix(ref addr) => getsockname_sockaddr(sockfd, addr) + } +}