summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md5
-rw-r--r--src/sys/socket/sockopt.rs61
-rw-r--r--test/sys/test_sockopt.rs25
3 files changed, 91 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d974c7a1..f89e4998 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
+- Added support of CString type in `setsockopt`.
+ ([#972](https://github.com/nix-rust/nix/pull/972))
+- Added option `TCP_CONGESTION` in `setsockopt`.
+ ([#972](https://github.com/nix-rust/nix/pull/972))
+
### Changed
### Fixed
### Removed
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index 494de4f5..17119384 100644
--- a/src/sys/socket/sockopt.rs
+++ b/src/sys/socket/sockopt.rs
@@ -5,6 +5,14 @@ use sys::time::TimeVal;
use libc::{self, c_int, uint8_t, c_void, socklen_t};
use std::mem;
use std::os::unix::io::RawFd;
+use std::ffi::{OsStr, OsString};
+#[cfg(target_family = "unix")]
+use std::os::unix::ffi::OsStrExt;
+
+// Constants
+// TCP_CA_NAME_MAX isn't defined in user space include files
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+const TCP_CA_NAME_MAX: usize = 16;
/// Helper for implementing `SetSockOpt` for a given socket option. See
/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html).
@@ -152,6 +160,10 @@ macro_rules! sockopt_impl {
sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize);
};
+ (Both, $name:ident, $level:path, $flag:path, OsString<$array:ty>) => {
+ sockopt_impl!(Both, $name, $level, $flag, OsString, GetOsString<$array>, SetOsString);
+ };
+
/*
* Matchers with generic getter types must be placed at the end, so
* they'll only match _after_ specialized matchers fail
@@ -257,6 +269,8 @@ sockopt_impl!(Both, BindAny, libc::IPPROTO_IP, libc::IP_BINDANY, bool);
sockopt_impl!(Both, Mark, libc::SOL_SOCKET, libc::SO_MARK, u32);
#[cfg(any(target_os = "android", target_os = "linux"))]
sockopt_impl!(Both, PassCred, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>);
/*
*
@@ -478,6 +492,53 @@ unsafe impl<'a> Set<'a, usize> for SetUsize {
}
}
+/// Getter for a `OsString` value.
+struct GetOsString<T: AsMut<[u8]>> {
+ len: socklen_t,
+ val: T,
+}
+
+unsafe impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
+ unsafe fn blank() -> Self {
+ GetOsString {
+ len: mem::size_of::<T>() as socklen_t,
+ val: mem::zeroed(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ &mut self.val as *mut T as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn unwrap(mut self) -> OsString {
+ OsStr::from_bytes(self.val.as_mut()).to_owned()
+ }
+}
+
+/// Setter for a `OsString` value.
+struct SetOsString<'a> {
+ val: &'a OsStr,
+}
+
+unsafe impl<'a> Set<'a, OsString> for SetOsString<'a> {
+ fn new(val: &'a OsString) -> SetOsString {
+ SetOsString { val: val.as_os_str() }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ self.val.as_bytes().as_ptr() as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ self.val.len() as socklen_t
+ }
+}
+
+
#[cfg(test)]
mod test {
#[cfg(any(target_os = "android", target_os = "linux"))]
diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs
index a38657c1..efe2c56b 100644
--- a/test/sys/test_sockopt.rs
+++ b/test/sys/test_sockopt.rs
@@ -13,3 +13,28 @@ fn test_so_buf() {
let actual = getsockopt(fd, sockopt::RcvBuf).unwrap();
assert!(actual >= bufsize);
}
+
+// The CI doesn't supported getsockopt and setsockopt on emulated processors.
+// It's beleived that a QEMU issue, the tests run ok on a fully emulated system.
+// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
+// So the syscall doesn't work properly unless the kernel is also emulated.
+#[test]
+#[cfg(all(
+ any(target_arch = "x86", target_arch = "x86_64"),
+ any(target_os = "freebsd", target_os = "linux")
+))]
+fn test_tcp_congestion() {
+ use std::ffi::OsString;
+
+ let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
+
+ let val = getsockopt(fd, sockopt::TcpCongestion).unwrap();
+ setsockopt(fd, sockopt::TcpCongestion, &val).unwrap();
+
+ setsockopt(fd, sockopt::TcpCongestion, &OsString::from("tcp_congestion_does_not_exist")).unwrap_err();
+
+ assert_eq!(
+ getsockopt(fd, sockopt::TcpCongestion).unwrap(),
+ val
+ );
+}