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