1use std::cell::{Cell, RefCell, RefMut};
2use std::io;
3use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
4use std::time::Duration;
5
6use mio::event::Source;
7use rustc_target::spec::Os;
8
9use crate::shims::files::{FileDescription, FileDescriptionRef};
10use crate::shims::unix::UnixFileDescription;
11use crate::shims::unix::socket::{EvalContextExt, UnixSocketFileDescription};
12use crate::*;
13
14#[derive(Debug)]
15pub(super) struct UdpSocket {
16 family: socket2::Domain,
19 socket: RefCell<mio::net::UdpSocket>,
21 is_non_block: Cell<bool>,
23 is_bound: Cell<bool>,
25 io_readiness: RefCell<Readiness>,
27 read_timeout: Cell<Option<Duration>>,
33 write_timeout: Cell<Option<Duration>>,
39 watched: ReadinessWatched,
41}
42
43impl UdpSocket {
44 pub fn new<'tcx>(
45 family: socket2::Domain,
46 is_non_block: bool,
47 ecx: &mut MiriInterpCx<'tcx>,
48 ) -> io::Result<FileDescriptionRef<Self>> {
49 let socket =
50 socket2::Socket::new(family, socket2::Type::DGRAM, Some(socket2::Protocol::UDP))?;
51 socket.set_nonblocking(true)?;
54
55 let socket = cfg_select! {
56 unix => {{
59 use std::os::fd::OwnedFd;
60 mio::net::UdpSocket::from(OwnedFd::from(socket))
61 }},
62 windows => {{
63 use std::os::windows::io::OwnedSocket;
64 mio::net::UdpSocket::from(OwnedSocket::from(socket))
65 }},
66 _ => unreachable!("unsupported host platform"),
67 };
68
69 let udp_socket = UdpSocket {
70 family,
71 socket: RefCell::new(socket),
72 is_non_block: Cell::new(is_non_block),
73 is_bound: Cell::new(false),
74 io_readiness: RefCell::new(Readiness::EMPTY),
78 read_timeout: Cell::new(None),
79 write_timeout: Cell::new(None),
80 watched: ReadinessWatched::default(),
81 };
82
83 let fd = ecx.machine.fds.new_ref(udp_socket);
84
85 ecx.machine.blocking_io.register(fd.clone());
88
89 Ok(fd)
90 }
91}
92
93impl FileDescription for UdpSocket {
94 fn name(&self) -> &'static str {
95 "udp socket"
96 }
97
98 fn as_unix(self: FileDescriptionRef<Self>) -> FileDescriptionRef<dyn UnixFileDescription> {
99 self
100 }
101
102 fn readiness_watched(&self) -> Option<&ReadinessWatched> {
103 Some(&self.watched)
104 }
105
106 fn readiness(&self) -> Readiness {
107 *self.io_readiness.borrow()
108 }
109}
110
111impl UnixFileDescription for UdpSocket {
112 fn get_flags<'tcx>(&self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Scalar> {
113 let mut flags = ecx.eval_libc_i32("O_RDWR");
114
115 if self.is_non_block.get() {
116 flags |= ecx.eval_libc_i32("O_NONBLOCK");
117 }
118
119 interp_ok(Scalar::from_i32(flags))
120 }
121
122 fn set_flags<'tcx>(
123 &self,
124 mut flag: i32,
125 ecx: &mut MiriInterpCx<'tcx>,
126 ) -> InterpResult<'tcx, Scalar> {
127 let o_nonblock = ecx.eval_libc_i32("O_NONBLOCK");
128
129 if flag & o_nonblock == o_nonblock {
131 self.is_non_block.set(true);
132 flag &= !o_nonblock;
133 } else {
134 self.is_non_block.set(false);
135 }
136
137 if flag != 0 {
139 throw_unsup_format!("fcntl: only O_NONBLOCK is supported for sockets")
140 }
141
142 interp_ok(Scalar::from_i32(0))
143 }
144
145 fn as_socket(
146 self: FileDescriptionRef<Self>,
147 ) -> Option<FileDescriptionRef<dyn UnixSocketFileDescription>> {
148 Some(self)
149 }
150}
151
152impl UnixSocketFileDescription for UdpSocket {
153 fn bind<'tcx>(
154 self: FileDescriptionRef<Self>,
155 communicate_allowed: bool,
156 address: SocketAddr,
157 ecx: &mut MiriInterpCx<'tcx>,
158 ) -> InterpResult<'tcx, Result<(), IoError>> {
159 assert!(communicate_allowed, "cannot have `UdpSocket` with isolation enabled!");
160
161 let address_family = match &address {
162 SocketAddr::V4(_) => socket2::Domain::IPV4,
163 SocketAddr::V6(_) => socket2::Domain::IPV6,
164 };
165
166 if self.family != address_family {
167 let err = if matches!(ecx.tcx.sess.target.os, Os::Linux | Os::Android | Os::MacOs) {
170 LibcError("EINVAL")
174 } else {
175 LibcError("EAFNOSUPPORT")
179 };
180 return interp_ok(Err(err));
181 }
182
183 if let Err(e) = self.with_socket_ref(|s| s.bind(&socket2::SockAddr::from(address))).unwrap()
185 {
186 return interp_ok(Err(IoError::HostError(e)));
187 }
188
189 self.is_bound.set(true);
191
192 interp_ok(Ok(()))
193 }
194
195 fn connect<'tcx>(
196 self: FileDescriptionRef<Self>,
197 communicate_allowed: bool,
198 address: SocketAddr,
199 ecx: &mut MiriInterpCx<'tcx>,
200 finish: DynMachineCallback<'tcx, Result<(), IoError>>,
201 ) -> InterpResult<'tcx> {
202 assert!(communicate_allowed, "cannot have `UdpSocket` with isolation enabled!");
203
204 if let Err(e) = self.socket.borrow().connect(address) {
205 return finish.call(ecx, Err(IoError::HostError(e)));
206 }
207
208 self.is_bound.set(true);
212
213 finish.call(ecx, Ok(()))
214 }
215
216 fn setsockopt<'tcx>(
217 self: FileDescriptionRef<Self>,
218 level: i32,
219 option: i32,
220 value_ptr: Pointer,
221 value_len: u64,
222 ecx: &mut MiriInterpCx<'tcx>,
223 ) -> InterpResult<'tcx, Result<(), IoError>> {
224 if level == ecx.eval_libc_i32("SOL_SOCKET") {
225 return ecx.set_generic_socket_option(option, value_ptr, value_len, self);
226 } else if level == ecx.eval_libc_i32("IPPROTO_IP") {
227 let socket = self.socket.borrow();
228 return ecx.set_ip_socket_option(
229 option,
230 value_ptr,
231 value_len,
232 socket2::SockRef::from(&*socket),
233 );
234 }
235
236 throw_unsup_format!(
237 "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET and IPPROTO_IP \
238 are allowed"
239 );
240 }
241
242 fn getsockopt<'tcx>(
243 self: FileDescriptionRef<Self>,
244 level: i32,
245 option: i32,
246 ecx: &mut MiriInterpCx<'tcx>,
247 ) -> InterpResult<'tcx, Result<MPlaceTy<'tcx>, IoError>> {
248 if level == ecx.eval_libc_i32("SOL_SOCKET") {
249 ecx.get_generic_socket_option(option, self)
250 } else if level == ecx.eval_libc_i32("IPPROTO_IP") {
251 let socket = self.socket.borrow();
252 ecx.get_ip_socket_option(option, socket2::SockRef::from(&*socket))
253 } else {
254 throw_unsup_format!(
255 "getsockopt: level {level:#x} is unsupported, only SOL_SOCKET and IPPROTO_IP \
256 are allowed"
257 )
258 }
259 }
260
261 fn getsockname<'tcx>(
262 self: FileDescriptionRef<Self>,
263 communicate_allowed: bool,
264 _ecx: &mut MiriInterpCx<'tcx>,
265 ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
266 assert!(communicate_allowed, "cannot have `UdpSocket` with isolation enabled!");
267
268 if !self.is_bound.get() {
269 let address = if self.family == socket2::Domain::IPV4 {
274 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))
275 } else {
276 SocketAddr::V6(SocketAddrV6::new(
277 Ipv6Addr::UNSPECIFIED,
278 0,
279 0,
280 0,
281 ))
282 };
283 return interp_ok(Ok(address));
284 }
285
286 interp_ok(self.socket.borrow().local_addr().map_err(IoError::HostError))
287 }
288
289 fn getpeername<'tcx>(
290 self: FileDescriptionRef<Self>,
291 communicate_allowed: bool,
292 ecx: &mut MiriInterpCx<'tcx>,
293 finish: DynMachineCallback<'tcx, Result<SocketAddr, IoError>>,
294 ) -> InterpResult<'tcx> {
295 assert!(communicate_allowed, "cannot have `UdpSocket` with isolation enabled!");
296
297 finish.call(ecx, self.socket.borrow().peer_addr().map_err(IoError::HostError))
298 }
299
300 fn set_read_timeout<'tcx>(
301 self: FileDescriptionRef<Self>,
302 timeout: Option<Duration>,
303 ) -> InterpResult<'tcx> {
304 self.read_timeout.set(timeout);
305 interp_ok(())
306 }
307
308 fn get_read_timeout<'tcx>(
309 self: FileDescriptionRef<Self>,
310 ) -> InterpResult<'tcx, Option<Duration>> {
311 interp_ok(self.read_timeout.get())
312 }
313
314 fn set_write_timeout<'tcx>(
315 self: FileDescriptionRef<Self>,
316 timeout: Option<Duration>,
317 ) -> InterpResult<'tcx> {
318 self.write_timeout.set(timeout);
319 interp_ok(())
320 }
321
322 fn get_write_timeout<'tcx>(
323 self: FileDescriptionRef<Self>,
324 ) -> InterpResult<'tcx, Option<Duration>> {
325 interp_ok(self.write_timeout.get())
326 }
327
328 fn with_socket_ref<T>(&self, cb: impl FnOnce(socket2::SockRef<'_>) -> T) -> Result<T, ()>
329 where
330 Self: Sized,
331 {
332 let socket = self.socket.borrow();
333 Ok(cb(socket2::SockRef::from(&*socket)))
334 }
335}
336
337impl SourceFileDescription for UdpSocket {
338 fn with_source(&self, f: &mut dyn FnMut(&mut dyn Source) -> io::Result<()>) -> io::Result<()> {
339 let mut socket = self.socket.borrow_mut();
340 f(&mut *socket)
341 }
342
343 fn get_readiness_mut(&self) -> RefMut<'_, Readiness> {
344 self.io_readiness.borrow_mut()
345 }
346}