• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::io;
2 use std::mem::size_of;
3 use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
4 
new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int>5 pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> {
6     let domain = match addr {
7         SocketAddr::V4(..) => libc::AF_INET,
8         SocketAddr::V6(..) => libc::AF_INET6,
9     };
10 
11     new_socket(domain, socket_type)
12 }
13 
14 /// Create a new non-blocking socket.
new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result<libc::c_int>15 pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result<libc::c_int> {
16     #[cfg(any(
17         target_os = "android",
18         target_os = "dragonfly",
19         target_os = "freebsd",
20         target_os = "illumos",
21         target_os = "linux",
22         target_os = "netbsd",
23         target_os = "openbsd"
24     ))]
25     let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
26 
27     // Gives a warning for platforms without SOCK_NONBLOCK.
28     #[allow(clippy::let_and_return)]
29     let socket = syscall!(socket(domain, socket_type, 0));
30 
31     // Mimick `libstd` and set `SO_NOSIGPIPE` on apple systems.
32     #[cfg(target_vendor = "apple")]
33     let socket = socket.and_then(|socket| {
34         syscall!(setsockopt(
35             socket,
36             libc::SOL_SOCKET,
37             libc::SO_NOSIGPIPE,
38             &1 as *const libc::c_int as *const libc::c_void,
39             size_of::<libc::c_int>() as libc::socklen_t
40         ))
41         .map(|_| socket)
42     });
43 
44     // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. Not sure about
45     // Solaris, couldn't find anything online.
46     #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))]
47     let socket = socket.and_then(|socket| {
48         // For platforms that don't support flags in socket, we need to
49         // set the flags ourselves.
50         syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))
51             .and_then(|_| syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| socket))
52             .map_err(|e| {
53                 // If either of the `fcntl` calls failed, ensure the socket is
54                 // closed and return the error.
55                 let _ = syscall!(close(socket));
56                 e
57             })
58     });
59 
60     socket
61 }
62 
63 /// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level
64 /// SocketAddr* types into their system representation. The benefit of this specific
65 /// type over using `libc::sockaddr_storage` is that this type is exactly as large as it
66 /// needs to be and not a lot larger. And it can be initialized cleaner from Rust.
67 #[repr(C)]
68 pub(crate) union SocketAddrCRepr {
69     v4: libc::sockaddr_in,
70     v6: libc::sockaddr_in6,
71 }
72 
73 impl SocketAddrCRepr {
as_ptr(&self) -> *const libc::sockaddr74     pub(crate) fn as_ptr(&self) -> *const libc::sockaddr {
75         self as *const _ as *const libc::sockaddr
76     }
77 }
78 
79 /// Converts a Rust `SocketAddr` into the system representation.
socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t)80 pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) {
81     match addr {
82         SocketAddr::V4(ref addr) => {
83             // `s_addr` is stored as BE on all machine and the array is in BE order.
84             // So the native endian conversion method is used so that it's never swapped.
85             let sin_addr = libc::in_addr {
86                 s_addr: u32::from_ne_bytes(addr.ip().octets()),
87             };
88 
89             let sockaddr_in = libc::sockaddr_in {
90                 sin_family: libc::AF_INET as libc::sa_family_t,
91                 sin_port: addr.port().to_be(),
92                 sin_addr,
93                 sin_zero: [0; 8],
94                 #[cfg(any(
95                     target_os = "dragonfly",
96                     target_os = "freebsd",
97                     target_os = "ios",
98                     target_os = "macos",
99                     target_os = "netbsd",
100                     target_os = "openbsd"
101                 ))]
102                 sin_len: 0,
103             };
104 
105             let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
106             let socklen = size_of::<libc::sockaddr_in>() as libc::socklen_t;
107             (sockaddr, socklen)
108         }
109         SocketAddr::V6(ref addr) => {
110             let sockaddr_in6 = libc::sockaddr_in6 {
111                 sin6_family: libc::AF_INET6 as libc::sa_family_t,
112                 sin6_port: addr.port().to_be(),
113                 sin6_addr: libc::in6_addr {
114                     s6_addr: addr.ip().octets(),
115                 },
116                 sin6_flowinfo: addr.flowinfo(),
117                 sin6_scope_id: addr.scope_id(),
118                 #[cfg(any(
119                     target_os = "dragonfly",
120                     target_os = "freebsd",
121                     target_os = "ios",
122                     target_os = "macos",
123                     target_os = "netbsd",
124                     target_os = "openbsd"
125                 ))]
126                 sin6_len: 0,
127                 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
128                 __sin6_src_id: 0,
129             };
130 
131             let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
132             let socklen = size_of::<libc::sockaddr_in6>() as libc::socklen_t;
133             (sockaddr, socklen)
134         }
135     }
136 }
137 
138 /// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`.
139 ///
140 /// # Safety
141 ///
142 /// `storage` must have the `ss_family` field correctly initialized.
143 /// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`.
to_socket_addr( storage: *const libc::sockaddr_storage, ) -> io::Result<SocketAddr>144 pub(crate) unsafe fn to_socket_addr(
145     storage: *const libc::sockaddr_storage,
146 ) -> io::Result<SocketAddr> {
147     match (*storage).ss_family as libc::c_int {
148         libc::AF_INET => {
149             // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
150             let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in);
151             let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes());
152             let port = u16::from_be(addr.sin_port);
153             Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
154         }
155         libc::AF_INET6 => {
156             // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
157             let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6);
158             let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr);
159             let port = u16::from_be(addr.sin6_port);
160             Ok(SocketAddr::V6(SocketAddrV6::new(
161                 ip,
162                 port,
163                 addr.sin6_flowinfo,
164                 addr.sin6_scope_id,
165             )))
166         }
167         _ => Err(io::ErrorKind::InvalidInput.into()),
168     }
169 }
170