use {Error, Result, 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};
use std::{mem, ptr};
use std::os::unix::io::RawFd;
mod addr;
mod consts;
mod ffi;
mod multicast;
pub mod sockopt;
pub use self::addr::{
AddressFamily,
SockAddr,
InetAddr,
UnixAddr,
IpAddr,
Ipv4Addr,
Ipv6Addr,
};
pub use libc::{
in_addr,
in6_addr,
sockaddr,
sockaddr_in,
sockaddr_in6,
sockaddr_un,
sa_family_t,
};
pub use self::multicast::{
ip_mreq,
ipv6_mreq,
};
pub use self::consts::*;
#[cfg(any(not(target_os = "linux"), not(target_arch = "x86")))]
pub use libc::sockaddr_storage;
#[cfg(all(target_os = "linux", target_arch = "x86"))]
pub struct sockaddr_storage {
pub ss_family: sa_family_t,
pub __ss_align: u32,
pub __ss_pad2: [u8; 120],
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
pub enum SockType {
Stream = consts::SOCK_STREAM,
Datagram = consts::SOCK_DGRAM,
SeqPacket = consts::SOCK_SEQPACKET,
Raw = consts::SOCK_RAW,
Rdm = consts::SOCK_RDM,
}
bitflags!(
flags SockFlag: c_int {
const SOCK_NONBLOCK = 0o0004000,
const SOCK_CLOEXEC = 0o2000000
}
);
pub fn socket(domain: AddressFamily, ty: SockType, flags: SockFlag) -> Result<RawFd> {
let mut ty = ty as c_int;
let feat_atomic = features::socket_atomic_cloexec();
if feat_atomic {
ty = ty | flags.bits();
}
let res = unsafe { ffi::socket(domain as c_int, ty, 0) };
if res < 0 {
return Err(Error::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 socketpair(domain: AddressFamily, ty: SockType, protocol: c_int,
flags: SockFlag) -> Result<(RawFd, RawFd)> {
let mut ty = ty as c_int;
let feat_atomic = features::socket_atomic_cloexec();
if feat_atomic {
ty = ty | flags.bits();
}
let mut fds = [-1, -1];
let res = unsafe {
ffi::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr())
};
if res < 0 {
return Err(Error::Sys(Errno::last()));
}
if !feat_atomic {
if flags.contains(SOCK_CLOEXEC) {
try!(fcntl(fds[0], F_SETFD(FD_CLOEXEC)));
try!(fcntl(fds[1], F_SETFD(FD_CLOEXEC)));
}
if flags.contains(SOCK_NONBLOCK) {
try!(fcntl(fds[0], F_SETFL(O_NONBLOCK)));
try!(fcntl(fds[1], F_SETFL(O_NONBLOCK)));
}
}
Ok((fds[0], fds[1]))
}
pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> {
let res = unsafe { ffi::listen(sockfd, backlog as c_int) };
from_ffi(res)
}
pub fn bind(fd: RawFd, addr: &SockAddr) -> Result<()> {
let res = unsafe {
let (ptr, len) = addr.as_ffi_pair();
ffi::bind(fd, ptr, len)
};
from_ffi(res)
}
pub fn accept(sockfd: RawFd) -> Result<RawFd> {
let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };
if res < 0 {
return Err(Error::Sys(Errno::last()));
}
Ok(res)
}
pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> {
accept4_polyfill(sockfd, flags)
}
#[inline]
fn accept4_polyfill(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> {
let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };
if res < 0 {
return Err(Error::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(fd: RawFd, addr: &SockAddr) -> Result<()> {
let res = unsafe {
let (ptr, len) = addr.as_ffi_pair();
ffi::connect(fd, ptr, len)
};
from_ffi(res)
}
pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: SockMessageFlags) -> Result<usize> {
unsafe {
let ret = ffi::recv(
sockfd,
buf.as_ptr() as *mut c_void,
buf.len() as size_t,
flags);
if ret < 0 {
Err(Error::last())
} else {
Ok(ret as usize)
}
}
}
pub fn recvfrom(sockfd: RawFd, buf: &mut [u8]) -> Result<(usize, SockAddr)> {
unsafe {
let addr: sockaddr_storage = mem::zeroed();
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
let ret = ffi::recvfrom(
sockfd,
buf.as_ptr() as *mut c_void,
buf.len() as size_t,
0,
mem::transmute(&addr),
&mut len as *mut socklen_t);
if ret < 0 {
return Err(Error::last());
}
sockaddr_storage_to_addr(&addr, len as usize)
.map(|addr| (ret as usize, addr))
}
}
pub fn sendto(fd: RawFd, buf: &[u8], addr: &SockAddr, flags: SockMessageFlags) -> Result<usize> {
let ret = unsafe {
let (ptr, len) = addr.as_ffi_pair();
ffi::sendto(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags, ptr, len)
};
if ret < 0 {
Err(Error::Sys(Errno::last()))
} else {
Ok(ret as usize)
}
}
pub fn send(fd: RawFd, buf: &[u8], flags: SockMessageFlags) -> Result<usize> {
let ret = unsafe {
ffi::send(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags)
};
if ret < 0 {
Err(Error::last())
} else {
Ok(ret as usize)
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct linger {
pub l_onoff: c_int,
pub l_linger: c_int
}
#[repr(i32)]
pub enum SockLevel {
Socket = SOL_SOCKET,
Tcp = IPPROTO_TCP,
Ip = IPPROTO_IP,
Ipv6 = IPPROTO_IPV6,
Udp = IPPROTO_UDP,
}
pub trait GetSockOpt : Copy {
type Val;
#[doc(hidden)]
fn get(&self, fd: RawFd) -> Result<Self::Val>;
}
pub trait SetSockOpt : Copy {
type Val;
#[doc(hidden)]
fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>;
}
pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> {
opt.get(fd)
}
pub fn setsockopt<O: SetSockOpt>(fd: RawFd, opt: O, val: &O::Val) -> Result<()> {
opt.set(fd, val)
}
pub fn getpeername(fd: RawFd) -> Result<SockAddr> {
unsafe {
let addr: sockaddr_storage = mem::uninitialized();
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
let ret = ffi::getpeername(fd, mem::transmute(&addr), &mut len);
if ret < 0 {
return Err(Error::last());
}
sockaddr_storage_to_addr(&addr, len as usize)
}
}
pub fn getsockname(fd: RawFd) -> Result<SockAddr> {
unsafe {
let addr: sockaddr_storage = mem::uninitialized();
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
let ret = ffi::getsockname(fd, mem::transmute(&addr), &mut len);
if ret < 0 {
return Err(Error::last());
}
sockaddr_storage_to_addr(&addr, len as usize)
}
}
pub unsafe fn sockaddr_storage_to_addr(
addr: &sockaddr_storage,
len: usize) -> Result<SockAddr> {
match addr.ss_family as c_int {
consts::AF_INET => {
assert!(len as usize == mem::size_of::<sockaddr_in>());
let ret = *(addr as *const _ as *const sockaddr_in);
Ok(SockAddr::Inet(InetAddr::V4(ret)))
}
consts::AF_INET6 => {
assert!(len as usize == mem::size_of::<sockaddr_in6>());
Ok(SockAddr::Inet(InetAddr::V6((*(addr as *const _ as *const sockaddr_in6)))))
}
consts::AF_UNIX => {
assert!(len as usize == mem::size_of::<sockaddr_un>());
Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un))))
}
af => panic!("unexpected address family {}", af),
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Shutdown {
Read,
Write,
Both,
}
pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {
unsafe {
use libc::shutdown;
let how = match how {
Shutdown::Read => consts::SHUT_RD,
Shutdown::Write => consts::SHUT_WR,
Shutdown::Both => consts::SHUT_RDWR,
};
let ret = shutdown(df, how);
if ret < 0 {
return Err(Error::last());
}
}
Ok(())
}
#[test]
pub fn test_struct_sizes() {
use nixtest;
nixtest::assert_size_of::<sockaddr_storage>("sockaddr_storage");
}