diff options
author | Carl Lerche <me@carllerche.com> | 2015-02-25 12:24:43 -0800 |
---|---|---|
committer | Carl Lerche <me@carllerche.com> | 2015-02-25 12:24:43 -0800 |
commit | 8a7cd5675d38365d96e322693f455b2cd7fea6e5 (patch) | |
tree | 860341241eddeda0168c06bfbdb2d6d1df1ec702 | |
parent | fcc952a18c054821480964bc1b0f7b32e7728e62 (diff) | |
download | nix-8a7cd5675d38365d96e322693f455b2cd7fea6e5.zip |
Large cleanup, mostly of socket functions
-rw-r--r-- | nix-test/src/sizes.c | 4 | ||||
-rw-r--r-- | src/lib.rs | 8 | ||||
-rw-r--r-- | src/nix.rs | 4 | ||||
-rw-r--r-- | src/sys/mod.rs | 3 | ||||
-rw-r--r-- | src/sys/socket/addr.rs | 127 | ||||
-rw-r--r-- | src/sys/socket/consts.rs | 44 | ||||
-rw-r--r-- | src/sys/socket/mod.rs | 255 | ||||
-rw-r--r-- | src/sys/socket/multicast.rs | 44 | ||||
-rw-r--r-- | src/sys/syscall.rs (renamed from src/syscall.rs) | 2 | ||||
-rw-r--r-- | src/sys/uio.rs | 3 | ||||
-rw-r--r-- | src/unistd.rs | 4 | ||||
-rw-r--r-- | test/sys/test_socket.rs | 21 | ||||
-rw-r--r-- | test/test.rs | 33 | ||||
-rw-r--r-- | test/test_nix_path.rs | 0 |
14 files changed, 372 insertions, 180 deletions
diff --git a/nix-test/src/sizes.c b/nix-test/src/sizes.c index 24c46fd2..8cf30d20 100644 --- a/nix-test/src/sizes.c +++ b/nix-test/src/sizes.c @@ -1,3 +1,4 @@ +#include "sys/socket.h" #include "sys/uio.h" #define SIZE_OF_T(TYPE) \ @@ -16,6 +17,9 @@ size_t size_of(const char* type) { + // sys/socket + SIZE_OF_S(sockaddr_storage); + // sys/uio SIZE_OF_S(iovec); @@ -1,3 +1,7 @@ +//! Rust friendly bindings to the various *nix system functions. +//! +//! Modules are structured according to the C header file that they would be +//! defined in. #![crate_name = "nix"] #![feature(collections, core, net, linkage, libc, os, path, std_misc)] @@ -18,6 +22,7 @@ pub use libc::{c_int, c_void}; mod nix; pub use nix::{NixResult, NixError, NixPath, from_ffi}; + #[cfg(unix)] pub mod errno; @@ -36,8 +41,5 @@ pub mod sched; #[cfg(unix)] pub mod sys; -#[cfg(target_os = "linux")] -pub mod syscall; - #[cfg(unix)] pub mod unistd; @@ -12,6 +12,10 @@ pub enum NixError { } impl NixError { + pub fn last() -> NixError { + NixError::Sys(Errno::last()) + } + pub fn invalid_argument() -> NixError { NixError::Sys(EINVAL) } diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 5dacde21..8e608d1e 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -17,6 +17,9 @@ pub mod socket; pub mod stat; +#[cfg(target_os = "linux")] +pub mod syscall; + #[cfg(not(target_os = "ios"))] pub mod termios; diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index 835979cf..5dda1194 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -1,13 +1,27 @@ use {NixResult, NixError}; -use super::{sa_family_t, in_addr, sockaddr_in, sockaddr_in6, sockaddr_un, AF_UNIX, AF_INET}; +use super::{consts, sa_family_t, in_addr, sockaddr_in, sockaddr_in6, sockaddr_un, AF_UNIX, AF_INET}; use errno::Errno; use libc; -use std::{mem, net, path, ptr}; +use std::{fmt, mem, net, path, ptr}; use std::ffi::{AsOsStr, CStr, OsStr}; use std::os::unix::OsStrExt; /* * + * ===== AddressFamily ===== + * + */ + +#[repr(i32)] +#[derive(Copy, PartialEq, Eq, Debug)] +pub enum AddressFamily { + Unix = consts::AF_UNIX, + Inet = consts::AF_INET, + Inet6 = consts::AF_INET6, +} + +/* + * * ===== Sock addr ===== * */ @@ -15,12 +29,46 @@ use std::os::unix::OsStrExt; /// Represents a socket address #[derive(Copy)] pub enum SockAddr { - // TODO: Rename these variants IpV4, IpV6, Unix IpV4(sockaddr_in), IpV6(sockaddr_in6), Unix(sockaddr_un) } +impl SockAddr { + pub fn family(&self) -> AddressFamily { + match *self { + SockAddr::IpV4(..) => AddressFamily::Inet, + SockAddr::IpV6(..) => AddressFamily::Inet6, + SockAddr::Unix(..) => AddressFamily::Unix, + } + } + + pub fn to_str(&self) -> String { + format!("{}", self) + } +} + +impl fmt::Display for SockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use std::num::Int; + + match *self { + SockAddr::IpV4(sin) => { + let ip = Int::from_be(sin.sin_addr.s_addr); + let port = Int::from_be(sin.sin_port); + + write!(f, "{}.{}.{}.{}:{}", + (ip >> 24) & 0xff, + (ip >> 16) & 0xff, + (ip >> 8) & 0xff, + (ip) & 0xff, + port) + } + _ => write!(f, "[some sock addr type... Debug is not implemented :(]"), + } + } +} + /// A trait for values which can be converted or resolved to a SockAddr. pub trait ToSockAddr { /// Converts the value to a SockAddr @@ -69,10 +117,18 @@ impl ToSockAddr for path::Path { } } +/// Convert a path buf into a unix domain socket address +impl ToSockAddr for path::PathBuf { + fn to_sock_addr(&self) -> NixResult<SockAddr> { + (**self).to_sock_addr() + } +} + /// Convert an inet address into a socket address impl ToSockAddr for net::SocketAddr { fn to_sock_addr(&self) -> NixResult<SockAddr> { use std::net::IpAddr; + use std::num::Int; match self.ip() { IpAddr::V4(ip) => { @@ -81,7 +137,7 @@ impl ToSockAddr for net::SocketAddr { Ok(SockAddr::IpV4(sockaddr_in { sin_family: AF_INET as sa_family_t, - sin_port: self.port(), + sin_port: self.port().to_be(), sin_addr: addr, .. unsafe { mem::zeroed() } })) @@ -110,7 +166,7 @@ impl FromSockAddr for net::SocketAddr { ((ip >> 8) as u8) & 0xff, ((ip >> 0) as u8) & 0xff); - Some(net::SocketAddr::new(IpAddr::V4(ip), addr.sin_port)) + Some(net::SocketAddr::new(IpAddr::V4(ip), Int::from_be(addr.sin_port))) } SockAddr::IpV6(_) => unimplemented!(), _ => None, @@ -134,10 +190,58 @@ impl FromSockAddr for path::PathBuf { /* * + * ===== IpAddr ===== + * + */ + +/// Convert to an IpAddr +pub trait ToIpAddr { + fn to_ip_addr(self) -> Option<net::IpAddr>; +} + +impl ToIpAddr for net::IpAddr { + fn to_ip_addr(self) -> Option<net::IpAddr> { + Some(self) + } +} + +impl<'a> ToIpAddr for &'a net::IpAddr { + fn to_ip_addr(self) -> Option<net::IpAddr> { + Some(*self) + } +} + +impl ToIpAddr for net::Ipv4Addr { + fn to_ip_addr(self) -> Option<net::IpAddr> { + Some(net::IpAddr::V4(self)) + } +} + +impl<'a> ToIpAddr for &'a net::Ipv4Addr { + fn to_ip_addr(self) -> Option<net::IpAddr> { + (*self).to_ip_addr() + } +} + +impl ToIpAddr for net::Ipv6Addr { + fn to_ip_addr(self) -> Option<net::IpAddr> { + Some(net::IpAddr::V6(self)) + } +} + +impl<'a> ToIpAddr for &'a net::Ipv6Addr { + fn to_ip_addr(self) -> Option<net::IpAddr> { + (*self).to_ip_addr() + } +} + +/* + * * ===== InAddr ===== * */ +/// Convert to an in_addr pub trait ToInAddr { fn to_in_addr(self) -> Option<libc::in_addr>; } @@ -183,13 +287,12 @@ impl ToInAddr for net::Ipv4Addr { use std::num::Int; let addr = self.octets(); - Some(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)) - }) + let ip = (((addr[0] as u32) << 24) | + ((addr[1] as u32) << 16) | + ((addr[2] as u32) << 8) | + ((addr[3] as u32) << 0)).to_be(); + + Some(in_addr { s_addr: ip }) } } diff --git a/src/sys/socket/consts.rs b/src/sys/socket/consts.rs index 39bcd733..3131cfb8 100644 --- a/src/sys/socket/consts.rs +++ b/src/sys/socket/consts.rs @@ -4,20 +4,16 @@ pub use self::os::*; mod os { use libc::{c_int, uint8_t}; - pub type AddressFamily = c_int; + pub const AF_UNIX: c_int = 1; + pub const AF_LOCAL: c_int = AF_UNIX; + pub const AF_INET: c_int = 2; + pub const AF_INET6: c_int = 10; - 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 const SOCK_STREAM: c_int = 1; + pub const SOCK_DGRAM: c_int = 2; + pub const SOCK_SEQPACKET: c_int = 5; + pub const SOCK_RAW: c_int = 3; + pub const SOCK_RDM: c_int = 4; pub const SOL_IP: c_int = 0; pub const SOL_SOCKET: c_int = 1; @@ -94,20 +90,16 @@ mod os { 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 AF_UNIX: c_int = 1; + pub const AF_LOCAL: c_int = AF_UNIX; + pub const AF_INET: c_int = 2; + pub const AF_INET6: c_int = 30; - 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 const SOCK_STREAM: c_int = 1; + pub const SOCK_DGRAM: c_int = 2; + pub const SOCK_SEQPACKET: c_int = 5; + pub const SOCK_RAW: c_int = 3; + pub const SOCK_RDM: c_int = 4; pub const SOL_SOCKET: c_int = 0xffff; pub const IPPROTO_IP: c_int = 0; diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index da29ae88..576bcaf4 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -1,3 +1,6 @@ +//! Socket interface functions +//! +//! [Further reading](http://man7.org/linux/man-pages/man7/socket.7.html) use {NixError, NixResult, from_ffi}; use errno::Errno; use features; @@ -23,6 +26,8 @@ pub use self::addr::{ SockAddr, ToSockAddr, FromSockAddr, + AddressFamily, + ToIpAddr, ToInAddr, }; pub use libc::{ @@ -40,6 +45,16 @@ pub use self::multicast::{ }; pub use self::consts::*; +#[derive(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, +} + // Extra flags - Supported by Linux 2.6.27, normalized on other platforms bitflags!( flags SockFlag: c_int { @@ -48,7 +63,11 @@ bitflags!( } ); -pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> NixResult<Fd> { +/// Create an endpoint for communication +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/socket.2.html) +pub fn socket(domain: AddressFamily, ty: SockType, flags: SockFlag) -> NixResult<Fd> { + let mut ty = ty as c_int; let feat_atomic = features::socket_atomic_cloexec(); if feat_atomic { @@ -56,7 +75,7 @@ pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> NixRe } // TODO: Check the kernel version - let res = unsafe { ffi::socket(domain, ty, 0) }; + let res = unsafe { ffi::socket(domain as c_int, ty, 0) }; if res < 0 { return Err(NixError::Sys(Errno::last())); @@ -75,11 +94,17 @@ pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> NixRe Ok(res) } +/// Listen for connections on a socket +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/listen.2.html) pub fn listen(sockfd: Fd, backlog: usize) -> NixResult<()> { let res = unsafe { ffi::listen(sockfd, backlog as c_int) }; from_ffi(res) } +/// Bind a name to a socket +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/bind.2.html) pub fn bind<A: ToSockAddr>(sockfd: Fd, addr: &A) -> NixResult<()> { let res = unsafe { try!(addr.with_sock_addr(|addr| { @@ -94,6 +119,9 @@ pub fn bind<A: ToSockAddr>(sockfd: Fd, addr: &A) -> NixResult<()> { from_ffi(res) } +/// Accept a connection on a socket +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html) pub fn accept(sockfd: Fd) -> NixResult<Fd> { let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) }; @@ -104,6 +132,9 @@ pub fn accept(sockfd: Fd) -> NixResult<Fd> { Ok(res) } +/// Accept a connection on a socket +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html) #[cfg(not(any(target_os = "macos", target_os = "ios")))] pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> { use libc::sockaddr; @@ -131,6 +162,9 @@ pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> { } } +/// Accept a connection on a socket +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html) #[cfg(any(target_os = "macos", target_os = "ios"))] pub fn accept4(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> { accept4_polyfill(sockfd, flags) @@ -155,6 +189,9 @@ fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> NixResult<Fd> { Ok(res) } +/// Initiate a connection on a socket +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/connect.2.html) pub fn connect<A: ToSockAddr>(sockfd: Fd, addr: &A) -> NixResult<()> { let res = unsafe { try!(addr.with_sock_addr(|addr| { @@ -169,69 +206,29 @@ pub fn connect<A: ToSockAddr>(sockfd: Fd, addr: &A) -> NixResult<()> { from_ffi(res) } -mod sa_helpers { - use std::mem; - use libc::{sockaddr_storage, sockaddr_in, sockaddr_in6, sockaddr_un}; - use super::SockAddr; - - pub fn to_sockaddr_ipv4(addr: &sockaddr_storage) -> SockAddr { - let sin : &sockaddr_in = unsafe { mem::transmute(addr) }; - SockAddr::IpV4( *sin ) - } - - pub fn to_sockaddr_ipv6(addr: &sockaddr_storage) -> SockAddr { - let sin6 : &sockaddr_in6 = unsafe { mem::transmute(addr) }; - SockAddr::IpV6( *sin6 ) - } - - pub fn to_sockaddr_unix(addr: &sockaddr_storage) -> SockAddr { - let sun : &sockaddr_un = unsafe { mem::transmute(addr) }; - SockAddr::Unix( *sun ) - } -} - +/// Receive data from a connectionless or connection-oriented socket. Returns +/// the number of bytes read and the socket address of the sender. +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/recvmsg.2.html) pub fn recvfrom(sockfd: Fd, buf: &mut [u8]) -> NixResult<(usize, SockAddr)> { - let saddr : sockaddr_storage = unsafe { mem::zeroed() }; - let mut len = mem::size_of::<sockaddr_storage>() 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); + unsafe { + let addr: sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; - write!(f, "{}.{}.{}.{}:{}", - (ip_addr >> 24) & 0xff, - (ip_addr >> 16) & 0xff, - (ip_addr >> 8) & 0xff, - (ip_addr) & 0xff, - port) -} + 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); -impl fmt::Debug for SockAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - SockAddr::IpV4(sin) => print_ipv4_addr(&sin, f), - _ => unimplemented!() + if ret < 0 { + return Err(NixError::last()); } + + sockaddr_storage_to_addr(&addr, len as usize) + .map(|addr| (ret as usize, addr)) } } @@ -249,12 +246,14 @@ fn sendto_sockaddr<T>(sockfd: Fd, buf: &[u8], flags: SockMessageFlags, addr: &T) } } -pub fn sendto(sockfd: Fd, buf: &[u8], addr: &SockAddr, flags: SockMessageFlags) -> NixResult<usize> { - let ret = match *addr { - SockAddr::IpV4(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), - SockAddr::IpV6(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), - SockAddr::Unix(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), - }; +pub fn sendto<A: ToSockAddr>(sockfd: Fd, buf: &[u8], addr: &A, flags: SockMessageFlags) -> NixResult<usize> { + let ret = try!(addr.with_sock_addr(|addr| { + match *addr { + SockAddr::IpV4(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), + SockAddr::IpV6(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), + SockAddr::Unix(ref addr) => sendto_sockaddr(sockfd, buf, flags, addr), + } + })); if ret < 0 { Err(NixError::Sys(Errno::last())) @@ -264,7 +263,7 @@ pub fn sendto(sockfd: Fd, buf: &[u8], addr: &SockAddr, flags: SockMessageFlags) } #[repr(C)] -#[derive(Copy)] +#[derive(Copy, Debug)] pub struct linger { pub l_onoff: c_int, pub l_linger: c_int @@ -276,7 +275,21 @@ pub struct linger { * */ -/// Represents a socket option that can be accessed or set +/// The protocol level at which to get / set socket options. Used as an +/// argument to `getsockopt` and `setsockopt`. +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html) +#[repr(i32)] +pub enum SockLevel { + Socket = SOL_SOCKET, + Tcp = IPPROTO_TCP, + Ip = IPPROTO_IP, + Ipv6 = IPPROTO_IPV6, + Udp = IPPROTO_UDP, +} + +/// Represents a socket option that can be accessed or set. Used as an argument +/// to `getsockopt` and `setsockopt`. pub trait SockOpt : Copy + fmt::Debug { /// Type of `getsockopt` return value type Get; @@ -292,78 +305,80 @@ pub trait SockOpt : Copy + fmt::Debug { fn set(&self, fd: Fd, level: c_int, val: Self::Set) -> NixResult<()>; } -pub enum SockLevel { - Socket, - Tcp, - Ip, - Ipv6, - Udp -} - -impl SockLevel { - fn as_cint(&self) -> c_int { - use self::SockLevel::*; - - match *self { - Socket => consts::SOL_SOCKET, - Tcp => consts::IPPROTO_TCP, - Ip => consts::IPPROTO_IP, - Ipv6 => consts::IPPROTO_IPV6, - Udp => consts::IPPROTO_UDP, - } - } -} - /// Get the current value for the requested socket option /// /// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html) pub fn getsockopt<O: SockOpt>(fd: Fd, level: SockLevel, opt: O) -> NixResult<O::Get> { - opt.get(fd, level.as_cint()) + opt.get(fd, level as c_int) } /// Sets the value for the requested socket option /// /// [Further reading](http://man7.org/linux/man-pages/man2/setsockopt.2.html) pub fn setsockopt<O: SockOpt>(fd: Fd, level: SockLevel, opt: O, val: O::Set) -> NixResult<()> { - opt.set(fd, level.as_cint(), val) + opt.set(fd, level as c_int, val) } -fn getpeername_sockaddr<T>(sockfd: Fd, addr: &T) -> NixResult<bool> { - let addrlen_expected = mem::size_of::<T>() as socklen_t; - let mut addrlen = addrlen_expected; +/// Get the address of the peer connected to the socket `fd`. +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/getpeername.2.html) +pub fn getpeername(fd: Fd) -> NixResult<SockAddr> { + unsafe { + let addr: sockaddr_storage = mem::uninitialized(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; - let ret = unsafe { ffi::getpeername(sockfd, mem::transmute(addr), &mut addrlen) }; - if ret < 0 { - return Err(NixError::Sys(Errno::last())); - } + let ret = ffi::getpeername(fd, mem::transmute(&addr), &mut len); - Ok(addrlen == addrlen_expected) -} + if ret < 0 { + return Err(NixError::last()); + } -pub fn getpeername(sockfd: Fd, addr: &mut SockAddr) -> NixResult<bool> { - match *addr { - SockAddr::IpV4(ref addr) => getpeername_sockaddr(sockfd, addr), - SockAddr::IpV6(ref addr) => getpeername_sockaddr(sockfd, addr), - SockAddr::Unix(ref addr) => getpeername_sockaddr(sockfd, addr) + sockaddr_storage_to_addr(&addr, len as usize) } } -fn getsockname_sockaddr<T>(sockfd: Fd, addr: &T) -> NixResult<bool> { - let addrlen_expected = mem::size_of::<T>() as socklen_t; - let mut addrlen = addrlen_expected; +/// Get the current address to which the socket `fd` is bound. +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/getsockname.2.html) +pub fn getsockname(fd: Fd) -> NixResult<SockAddr> { + unsafe { + let addr: sockaddr_storage = mem::uninitialized(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; - let ret = unsafe { ffi::getsockname(sockfd, mem::transmute(addr), &mut addrlen) }; - if ret < 0 { - return Err(NixError::Sys(Errno::last())); - } + let ret = ffi::getsockname(fd, mem::transmute(&addr), &mut len); - Ok(addrlen == addrlen_expected) + if ret < 0 { + return Err(NixError::last()); + } + + sockaddr_storage_to_addr(&addr, len as usize) + } } -pub fn getsockname(sockfd: Fd, addr: &mut SockAddr) -> NixResult<bool> { - match *addr { - SockAddr::IpV4(ref addr) => getsockname_sockaddr(sockfd, addr), - SockAddr::IpV6(ref addr) => getsockname_sockaddr(sockfd, addr), - SockAddr::Unix(ref addr) => getsockname_sockaddr(sockfd, addr) +unsafe fn sockaddr_storage_to_addr( + addr: &sockaddr_storage, + len: usize) -> NixResult<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::IpV4(ret)) + } + consts::AF_INET6 => { + assert!(len as usize == mem::size_of::<sockaddr_in6>()); + Ok(SockAddr::IpV6(*(addr as *const _ as *const sockaddr_in6))) + } + consts::AF_UNIX => { + assert!(len as usize == mem::size_of::<sockaddr_un>()); + Ok(SockAddr::Unix(*(addr as *const _ as *const sockaddr_un))) + } + af => panic!("unexpected address family {}", af), } } + +#[test] +pub fn test_struct_sizes() { + use nixtest; + nixtest::assert_size_of::<sockaddr_storage>("sockaddr_storage"); +} diff --git a/src/sys/socket/multicast.rs b/src/sys/socket/multicast.rs new file mode 100644 index 00000000..6831a915 --- /dev/null +++ b/src/sys/socket/multicast.rs @@ -0,0 +1,44 @@ + +use {NixResult, NixError}; +use super::addr::ToInAddr; +use super::consts; +use libc::in_addr; +use std::fmt; + +#[repr(C)] +#[derive(Copy)] +pub struct ip_mreq { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, +} + +impl fmt::Debug for ip_mreq { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "ip_mreq {{ imr_multiaddr: {{ s_addr: 0x{:x} }}, imr_interface: {{ s_addr: 0x{:x} }} }}", + self.imr_multiaddr.s_addr, self.imr_interface.s_addr) + } +} + +impl ip_mreq { + pub fn new<T: ToInAddr, U: ToInAddr>(group: T, interface: Option<U>) -> NixResult<ip_mreq> { + let group = match group.to_in_addr() { + Some(group) => group, + None => return Err(NixError::invalid_argument()), + }; + + let interface = match interface { + Some(interface) => { + match interface.to_in_addr() { + Some(interface) => interface, + None => return Err(NixError::invalid_argument()), + } + } + None => in_addr { s_addr: consts::INADDR_ANY }, + }; + + Ok(ip_mreq { + imr_multiaddr: group, + imr_interface: interface, + }) + } +} diff --git a/src/syscall.rs b/src/sys/syscall.rs index 9e4979d6..cdf6c414 100644 --- a/src/syscall.rs +++ b/src/sys/syscall.rs @@ -1,3 +1,5 @@ +//! Indirect system call +//! use libc::c_int; pub use self::arch::*; diff --git a/src/sys/uio.rs b/src/sys/uio.rs index 35ae6da3..0d2072b9 100644 --- a/src/sys/uio.rs +++ b/src/sys/uio.rs @@ -1,3 +1,6 @@ +// Silence invalid warnings due to rust-lang/rust#16719 +#![allow(improper_ctypes)] + use {NixResult, NixError}; use errno::Errno; use fcntl::Fd; diff --git a/src/unistd.rs b/src/unistd.rs index dd1d8d1a..a407b26c 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -1,3 +1,5 @@ +//! Standard symbolic constants and types +//! use {NixError, NixResult, NixPath, from_ffi}; use errno::Errno; use fcntl::{fcntl, Fd, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC}; @@ -354,7 +356,7 @@ pub fn unlink<P: NixPath>(path: P) -> NixResult<()> { #[cfg(target_os = "linux")] mod linux { - use syscall::{syscall, SYSPIVOTROOT}; + use sys::syscall::{syscall, SYSPIVOTROOT}; use errno::Errno; use {NixError, NixResult, NixPath}; diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 1c80fad9..588ba815 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -1,8 +1,10 @@ -use nix::sys::socket::{SockAddr, ToSockAddr, FromSockAddr}; +use nix::sys::socket::{SockAddr, ToSockAddr, FromSockAddr, getsockname}; use std::{mem, net}; use std::num::Int; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::os::unix::AsRawFd; +use ports::localhost; #[test] pub fn test_inetv4_addr_to_sock_addr() { @@ -11,12 +13,14 @@ pub fn test_inetv4_addr_to_sock_addr() { match addr { SockAddr::IpV4(addr) => { - assert_eq!(addr.sin_addr.s_addr, Int::from_be(2130706433)); - assert_eq!(addr.sin_port, 3000); + assert_eq!(addr.sin_addr.s_addr, 0x7f000001.to_be()); + assert_eq!(addr.sin_port, 3000.to_be()); } _ => panic!("nope"), } + assert_eq!(addr.to_str(), "127.0.0.1:3000"); + let inet = FromSockAddr::from_sock_addr(&addr).unwrap(); assert_eq!(actual, inet); } @@ -37,3 +41,14 @@ pub fn test_path_to_sock_addr() { let path: PathBuf = FromSockAddr::from_sock_addr(&addr).unwrap(); assert_eq!(actual, &*path); } + +#[test] +pub fn test_getsockname() { + use std::net::TcpListener; + + let addr = localhost(); + let sock = TcpListener::bind(&*addr).unwrap(); + let res = getsockname(sock.as_raw_fd()).unwrap(); + + assert_eq!(addr, res.to_str()); +} diff --git a/test/test.rs b/test/test.rs index 0fd45f0b..7c5a6b20 100644 --- a/test/test.rs +++ b/test/test.rs @@ -5,27 +5,30 @@ extern crate libc; extern crate rand; mod sys; +mod test_nix_path; mod test_stat; mod test_unistd; -use nix::NixPath; +mod ports { + use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; + use std::sync::atomic::Ordering::SeqCst; -#[test] -fn test_nix_path() { - fn cstr_to_bytes(cstr: &*const libc::c_char, len: usize) -> &[u8] { + // Helper for getting a unique port for the task run + // TODO: Reuse ports to not spam the system + static mut NEXT_PORT: AtomicUsize = ATOMIC_USIZE_INIT; + const FIRST_PORT: usize = 18080; + + pub fn next_port() -> usize { unsafe { - let cstr = cstr as *const _ as *const *const u8; - std::slice::from_raw_parts(*cstr, len) + // If the atomic was never used, set it to the initial port + NEXT_PORT.compare_and_swap(0, FIRST_PORT, SeqCst); + + // Get and increment the port list + NEXT_PORT.fetch_add(1, SeqCst) } } - let bytes = b"abcd"; - let ok = bytes.with_nix_path(|cstr| { - assert_eq!(b"abcd\0", cstr_to_bytes(&cstr, 5)); - }); - assert!(ok.is_ok()); - - let bytes = b"ab\0cd"; - let err = bytes.with_nix_path(|_| {}); - assert!(err.is_err()); + pub fn localhost() -> String { + format!("127.0.0.1:{}", next_port()) + } } diff --git a/test/test_nix_path.rs b/test/test_nix_path.rs new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/test_nix_path.rs |