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.
45 #[cfg(any(target_os = "ios", target_os = "macos"))]
46 let socket = socket.and_then(|socket| {
47 // For platforms that don't support flags in socket, we need to
48 // set the flags ourselves.
49 syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))
50 .and_then(|_| syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| socket))
51 .map_err(|e| {
52 // If either of the `fcntl` calls failed, ensure the socket is
53 // closed and return the error.
54 let _ = syscall!(close(socket));
55 e
56 })
57 });
58
59 socket
60 }
61
62 /// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level
63 /// SocketAddr* types into their system representation. The benefit of this specific
64 /// type over using `libc::sockaddr_storage` is that this type is exactly as large as it
65 /// needs to be and not a lot larger. And it can be initialized cleaner from Rust.
66 #[repr(C)]
67 pub(crate) union SocketAddrCRepr {
68 v4: libc::sockaddr_in,
69 v6: libc::sockaddr_in6,
70 }
71
72 impl SocketAddrCRepr {
as_ptr(&self) -> *const libc::sockaddr73 pub(crate) fn as_ptr(&self) -> *const libc::sockaddr {
74 self as *const _ as *const libc::sockaddr
75 }
76 }
77
78 /// Converts a Rust `SocketAddr` into the system representation.
socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t)79 pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) {
80 match addr {
81 SocketAddr::V4(ref addr) => {
82 // `s_addr` is stored as BE on all machine and the array is in BE order.
83 // So the native endian conversion method is used so that it's never swapped.
84 let sin_addr = libc::in_addr {
85 s_addr: u32::from_ne_bytes(addr.ip().octets()),
86 };
87
88 let sockaddr_in = libc::sockaddr_in {
89 sin_family: libc::AF_INET as libc::sa_family_t,
90 sin_port: addr.port().to_be(),
91 sin_addr,
92 sin_zero: [0; 8],
93 #[cfg(any(
94 target_os = "dragonfly",
95 target_os = "freebsd",
96 target_os = "ios",
97 target_os = "macos",
98 target_os = "netbsd",
99 target_os = "openbsd"
100 ))]
101 sin_len: 0,
102 };
103
104 let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
105 let socklen = size_of::<libc::sockaddr_in>() as libc::socklen_t;
106 (sockaddr, socklen)
107 }
108 SocketAddr::V6(ref addr) => {
109 let sockaddr_in6 = libc::sockaddr_in6 {
110 sin6_family: libc::AF_INET6 as libc::sa_family_t,
111 sin6_port: addr.port().to_be(),
112 sin6_addr: libc::in6_addr {
113 s6_addr: addr.ip().octets(),
114 },
115 sin6_flowinfo: addr.flowinfo(),
116 sin6_scope_id: addr.scope_id(),
117 #[cfg(any(
118 target_os = "dragonfly",
119 target_os = "freebsd",
120 target_os = "ios",
121 target_os = "macos",
122 target_os = "netbsd",
123 target_os = "openbsd"
124 ))]
125 sin6_len: 0,
126 #[cfg(target_os = "illumos")]
127 __sin6_src_id: 0,
128 };
129
130 let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
131 let socklen = size_of::<libc::sockaddr_in6>() as libc::socklen_t;
132 (sockaddr, socklen)
133 }
134 }
135 }
136
137 /// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`.
138 ///
139 /// # Safety
140 ///
141 /// `storage` must have the `ss_family` field correctly initialized.
142 /// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`.
to_socket_addr( storage: *const libc::sockaddr_storage, ) -> io::Result<SocketAddr>143 pub(crate) unsafe fn to_socket_addr(
144 storage: *const libc::sockaddr_storage,
145 ) -> io::Result<SocketAddr> {
146 match (*storage).ss_family as libc::c_int {
147 libc::AF_INET => {
148 // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
149 let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in);
150 let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes());
151 let port = u16::from_be(addr.sin_port);
152 Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
153 }
154 libc::AF_INET6 => {
155 // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
156 let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6);
157 let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr);
158 let port = u16::from_be(addr.sin6_port);
159 Ok(SocketAddr::V6(SocketAddrV6::new(
160 ip,
161 port,
162 addr.sin6_flowinfo,
163 addr.sin6_scope_id,
164 )))
165 }
166 _ => Err(io::ErrorKind::InvalidInput.into()),
167 }
168 }
169