1#[cfg(any(target_os = "android", target_os = "linux"))]
2use crate::*;
3use nix::sys::socket::{
4    getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag,
5    SockProtocol, SockType,
6};
7use rand::{thread_rng, Rng};
8
9// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
10#[cfg(any(target_os = "dragonfly", target_os = "freebsd",))]
11#[test]
12pub fn test_local_peercred_seqpacket() {
13    use nix::{
14        sys::socket::socketpair,
15        unistd::{Gid, Uid},
16    };
17
18    let (fd1, _fd2) = socketpair(
19        AddressFamily::Unix,
20        SockType::SeqPacket,
21        None,
22        SockFlag::empty(),
23    )
24    .unwrap();
25    let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
26    assert_eq!(xucred.version(), 0);
27    assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
28    assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
29}
30
31#[cfg(any(
32    target_os = "dragonfly",
33    target_os = "freebsd",
34    target_os = "macos",
35    target_os = "ios"
36))]
37#[test]
38pub fn test_local_peercred_stream() {
39    use nix::{
40        sys::socket::socketpair,
41        unistd::{Gid, Uid},
42    };
43
44    let (fd1, _fd2) = socketpair(
45        AddressFamily::Unix,
46        SockType::Stream,
47        None,
48        SockFlag::empty(),
49    )
50    .unwrap();
51    let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
52    assert_eq!(xucred.version(), 0);
53    assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
54    assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
55}
56
57#[cfg(target_os = "linux")]
58#[test]
59fn is_so_mark_functional() {
60    use nix::sys::socket::sockopt;
61
62    require_capability!("is_so_mark_functional", CAP_NET_ADMIN);
63
64    let s = socket(
65        AddressFamily::Inet,
66        SockType::Stream,
67        SockFlag::empty(),
68        None,
69    )
70    .unwrap();
71    setsockopt(s, sockopt::Mark, &1337).unwrap();
72    let mark = getsockopt(s, sockopt::Mark).unwrap();
73    assert_eq!(mark, 1337);
74}
75
76#[test]
77fn test_so_buf() {
78    let fd = socket(
79        AddressFamily::Inet,
80        SockType::Datagram,
81        SockFlag::empty(),
82        SockProtocol::Udp,
83    )
84    .unwrap();
85    let bufsize: usize = thread_rng().gen_range(4096..131_072);
86    setsockopt(fd, sockopt::SndBuf, &bufsize).unwrap();
87    let actual = getsockopt(fd, sockopt::SndBuf).unwrap();
88    assert!(actual >= bufsize);
89    setsockopt(fd, sockopt::RcvBuf, &bufsize).unwrap();
90    let actual = getsockopt(fd, sockopt::RcvBuf).unwrap();
91    assert!(actual >= bufsize);
92}
93
94#[test]
95fn test_so_tcp_maxseg() {
96    use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn};
97    use nix::unistd::{close, write};
98    use std::net::SocketAddrV4;
99    use std::str::FromStr;
100
101    let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap();
102    let sock_addr = SockaddrIn::from(std_sa);
103
104    let rsock = socket(
105        AddressFamily::Inet,
106        SockType::Stream,
107        SockFlag::empty(),
108        SockProtocol::Tcp,
109    )
110    .unwrap();
111    bind(rsock, &sock_addr).unwrap();
112    listen(rsock, 10).unwrap();
113    let initial = getsockopt(rsock, sockopt::TcpMaxSeg).unwrap();
114    // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some
115    // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
116    // than 700
117    cfg_if! {
118        if #[cfg(any(target_os = "android", target_os = "linux"))] {
119            let segsize: u32 = 873;
120            assert!(initial < segsize);
121            setsockopt(rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
122        } else {
123            assert!(initial < 700);
124        }
125    }
126
127    // Connect and check the MSS that was advertised
128    let ssock = socket(
129        AddressFamily::Inet,
130        SockType::Stream,
131        SockFlag::empty(),
132        SockProtocol::Tcp,
133    )
134    .unwrap();
135    connect(ssock, &sock_addr).unwrap();
136    let rsess = accept(rsock).unwrap();
137    write(rsess, b"hello").unwrap();
138    let actual = getsockopt(ssock, sockopt::TcpMaxSeg).unwrap();
139    // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
140    // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
141    cfg_if! {
142        if #[cfg(any(target_os = "android", target_os = "linux"))] {
143            assert!((segsize - 100) <= actual);
144            assert!(actual <= segsize);
145        } else {
146            assert!(initial < actual);
147            assert!(536 < actual);
148        }
149    }
150    close(rsock).unwrap();
151    close(ssock).unwrap();
152}
153
154#[test]
155fn test_so_type() {
156    let sockfd = socket(
157        AddressFamily::Inet,
158        SockType::Stream,
159        SockFlag::empty(),
160        None,
161    )
162    .unwrap();
163
164    assert_eq!(Ok(SockType::Stream), getsockopt(sockfd, sockopt::SockType));
165}
166
167/// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket
168/// types.  Regression test for https://github.com/nix-rust/nix/issues/1819
169#[cfg(any(target_os = "android", target_os = "linux",))]
170#[test]
171fn test_so_type_unknown() {
172    use nix::errno::Errno;
173
174    require_capability!("test_so_type", CAP_NET_RAW);
175    let sockfd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };
176    assert!(sockfd >= 0, "Error opening socket: {}", nix::Error::last());
177
178    assert_eq!(Err(Errno::EINVAL), getsockopt(sockfd, sockopt::SockType));
179}
180
181// The CI doesn't supported getsockopt and setsockopt on emulated processors.
182// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
183// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
184// So the syscall doesn't work properly unless the kernel is also emulated.
185#[test]
186#[cfg(all(
187    any(target_arch = "x86", target_arch = "x86_64"),
188    any(target_os = "freebsd", target_os = "linux")
189))]
190fn test_tcp_congestion() {
191    use std::ffi::OsString;
192
193    let fd = socket(
194        AddressFamily::Inet,
195        SockType::Stream,
196        SockFlag::empty(),
197        None,
198    )
199    .unwrap();
200
201    let val = getsockopt(fd, sockopt::TcpCongestion).unwrap();
202    setsockopt(fd, sockopt::TcpCongestion, &val).unwrap();
203
204    setsockopt(
205        fd,
206        sockopt::TcpCongestion,
207        &OsString::from("tcp_congestion_does_not_exist"),
208    )
209    .unwrap_err();
210
211    assert_eq!(getsockopt(fd, sockopt::TcpCongestion).unwrap(), val);
212}
213
214#[test]
215#[cfg(any(target_os = "android", target_os = "linux"))]
216fn test_bindtodevice() {
217    skip_if_not_root!("test_bindtodevice");
218
219    let fd = socket(
220        AddressFamily::Inet,
221        SockType::Stream,
222        SockFlag::empty(),
223        None,
224    )
225    .unwrap();
226
227    let val = getsockopt(fd, sockopt::BindToDevice).unwrap();
228    setsockopt(fd, sockopt::BindToDevice, &val).unwrap();
229
230    assert_eq!(getsockopt(fd, sockopt::BindToDevice).unwrap(), val);
231}
232
233#[test]
234fn test_so_tcp_keepalive() {
235    let fd = socket(
236        AddressFamily::Inet,
237        SockType::Stream,
238        SockFlag::empty(),
239        SockProtocol::Tcp,
240    )
241    .unwrap();
242    setsockopt(fd, sockopt::KeepAlive, &true).unwrap();
243    assert!(getsockopt(fd, sockopt::KeepAlive).unwrap());
244
245    #[cfg(any(
246        target_os = "android",
247        target_os = "dragonfly",
248        target_os = "freebsd",
249        target_os = "linux"
250    ))]
251    {
252        let x = getsockopt(fd, sockopt::TcpKeepIdle).unwrap();
253        setsockopt(fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap();
254        assert_eq!(getsockopt(fd, sockopt::TcpKeepIdle).unwrap(), x + 1);
255
256        let x = getsockopt(fd, sockopt::TcpKeepCount).unwrap();
257        setsockopt(fd, sockopt::TcpKeepCount, &(x + 1)).unwrap();
258        assert_eq!(getsockopt(fd, sockopt::TcpKeepCount).unwrap(), x + 1);
259
260        let x = getsockopt(fd, sockopt::TcpKeepInterval).unwrap();
261        setsockopt(fd, sockopt::TcpKeepInterval, &(x + 1)).unwrap();
262        assert_eq!(getsockopt(fd, sockopt::TcpKeepInterval).unwrap(), x + 1);
263    }
264}
265
266#[test]
267#[cfg(any(target_os = "android", target_os = "linux"))]
268#[cfg_attr(qemu, ignore)]
269fn test_get_mtu() {
270    use nix::sys::socket::{bind, connect, SockaddrIn};
271    use std::net::SocketAddrV4;
272    use std::str::FromStr;
273
274    let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap();
275    let std_sb = SocketAddrV4::from_str("127.0.0.1:4002").unwrap();
276
277    let usock = socket(
278        AddressFamily::Inet,
279        SockType::Datagram,
280        SockFlag::empty(),
281        SockProtocol::Udp,
282    )
283    .unwrap();
284
285    // Bind and initiate connection
286    bind(usock, &SockaddrIn::from(std_sa)).unwrap();
287    connect(usock, &SockaddrIn::from(std_sb)).unwrap();
288
289    // Loopback connections have 2^16 - the maximum - MTU
290    assert_eq!(getsockopt(usock, sockopt::IpMtu), Ok(u16::MAX as i32))
291}
292
293#[test]
294#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
295fn test_ttl_opts() {
296    let fd4 = socket(
297        AddressFamily::Inet,
298        SockType::Datagram,
299        SockFlag::empty(),
300        None,
301    )
302    .unwrap();
303    setsockopt(fd4, sockopt::Ipv4Ttl, &1)
304        .expect("setting ipv4ttl on an inet socket should succeed");
305    let fd6 = socket(
306        AddressFamily::Inet6,
307        SockType::Datagram,
308        SockFlag::empty(),
309        None,
310    )
311    .unwrap();
312    setsockopt(fd6, sockopt::Ipv6Ttl, &1)
313        .expect("setting ipv6ttl on an inet6 socket should succeed");
314}
315
316#[test]
317#[cfg(any(target_os = "ios", target_os = "macos"))]
318fn test_dontfrag_opts() {
319    let fd4 = socket(
320        AddressFamily::Inet,
321        SockType::Stream,
322        SockFlag::empty(),
323        SockProtocol::Tcp,
324    )
325    .unwrap();
326    setsockopt(fd4, sockopt::IpDontFrag, &true)
327        .expect("setting IP_DONTFRAG on an inet stream socket should succeed");
328    setsockopt(fd4, sockopt::IpDontFrag, &false).expect(
329        "unsetting IP_DONTFRAG on an inet stream socket should succeed",
330    );
331    let fd4d = socket(
332        AddressFamily::Inet,
333        SockType::Datagram,
334        SockFlag::empty(),
335        None,
336    )
337    .unwrap();
338    setsockopt(fd4d, sockopt::IpDontFrag, &true).expect(
339        "setting IP_DONTFRAG on an inet datagram socket should succeed",
340    );
341    setsockopt(fd4d, sockopt::IpDontFrag, &false).expect(
342        "unsetting IP_DONTFRAG on an inet datagram socket should succeed",
343    );
344}
345
346#[test]
347#[cfg(any(
348    target_os = "android",
349    target_os = "ios",
350    target_os = "linux",
351    target_os = "macos",
352))]
353// Disable the test under emulation because it fails in Cirrus-CI.  Lack
354// of QEMU support is suspected.
355#[cfg_attr(qemu, ignore)]
356fn test_v6dontfrag_opts() {
357    let fd6 = socket(
358        AddressFamily::Inet6,
359        SockType::Stream,
360        SockFlag::empty(),
361        SockProtocol::Tcp,
362    )
363    .unwrap();
364    setsockopt(fd6, sockopt::Ipv6DontFrag, &true).expect(
365        "setting IPV6_DONTFRAG on an inet6 stream socket should succeed",
366    );
367    setsockopt(fd6, sockopt::Ipv6DontFrag, &false).expect(
368        "unsetting IPV6_DONTFRAG on an inet6 stream socket should succeed",
369    );
370    let fd6d = socket(
371        AddressFamily::Inet6,
372        SockType::Datagram,
373        SockFlag::empty(),
374        None,
375    )
376    .unwrap();
377    setsockopt(fd6d, sockopt::Ipv6DontFrag, &true).expect(
378        "setting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
379    );
380    setsockopt(fd6d, sockopt::Ipv6DontFrag, &false).expect(
381        "unsetting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
382    );
383}
384
385#[test]
386#[cfg(target_os = "linux")]
387fn test_so_priority() {
388    let fd = socket(
389        AddressFamily::Inet,
390        SockType::Stream,
391        SockFlag::empty(),
392        SockProtocol::Tcp,
393    )
394    .unwrap();
395    let priority = 3;
396    setsockopt(fd, sockopt::Priority, &priority).unwrap();
397    assert_eq!(getsockopt(fd, sockopt::Priority).unwrap(), priority);
398}
399
400#[test]
401#[cfg(target_os = "linux")]
402fn test_ip_tos() {
403    let fd = socket(
404        AddressFamily::Inet,
405        SockType::Stream,
406        SockFlag::empty(),
407        SockProtocol::Tcp,
408    )
409    .unwrap();
410    let tos = 0x80; // CS4
411    setsockopt(fd, sockopt::IpTos, &tos).unwrap();
412    assert_eq!(getsockopt(fd, sockopt::IpTos).unwrap(), tos);
413}
414
415#[test]
416#[cfg(target_os = "linux")]
417// Disable the test under emulation because it fails in Cirrus-CI.  Lack
418// of QEMU support is suspected.
419#[cfg_attr(qemu, ignore)]
420fn test_ipv6_tclass() {
421    let fd = socket(
422        AddressFamily::Inet6,
423        SockType::Stream,
424        SockFlag::empty(),
425        SockProtocol::Tcp,
426    )
427    .unwrap();
428    let class = 0x80; // CS4
429    setsockopt(fd, sockopt::Ipv6TClass, &class).unwrap();
430    assert_eq!(getsockopt(fd, sockopt::Ipv6TClass).unwrap(), class);
431}
432