• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! # This module provides a safe Rust wrapper for the libslirp library.
16 
17 //! It allows to embed a virtual network stack within your Rust applications.
18 //!
19 //! ## Features
20 //!
21 //! * **Safe API:**  Wraps the libslirp C API in a safe and idiomatic Rust interface.
22 //! * **Networking:**  Provides functionality for virtual networking, including TCP/IP, UDP, and ICMP.
23 //! * **Proxy Support:**  Allows integration with proxy managers for handling external connections.
24 //! * **Threading:**  Handles communication between the Rust application and the libslirp event loop.
25 //!
26 //! ## Usage
27 //!
28 //! ```
29 //! use bytes::Bytes;
30 //! use libslirp_rs::libslirp_config::SlirpConfig;
31 //! use libslirp_rs::libslirp::LibSlirp;
32 //! use std::net::Ipv4Addr;
33 //! use std::sync::mpsc;
34 //!
35 //! let (tx_cmds, _) = mpsc::channel();
36 //! // Create a LibSlirp instance with default configuration
37 //! let libslirp = LibSlirp::new(
38 //!     SlirpConfig::default(),
39 //!     tx_cmds,
40 //!     None
41 //! );
42 //!
43 //! let data = vec![0x01, 0x02, 0x03];
44 //! // Input network data into libslirp
45 //! libslirp.input(Bytes::from(data));
46 //!
47 //! // ... other operations ...
48 //!
49 //! // Shutdown libslirp
50 //! libslirp.shutdown();
51 //! ```
52 //!
53 //! ## Example with Proxy
54 //!
55 //! ```
56 //! use libslirp_rs::libslirp::LibSlirp;
57 //! use libslirp_rs::libslirp_config::SlirpConfig;
58 //! use libslirp_rs::libslirp::{ProxyManager, ProxyConnect};
59 //! use std::sync::mpsc;
60 //! use std::net::SocketAddr;
61 //! // Implement the ProxyManager trait for your proxy logic
62 //! struct MyProxyManager;
63 //!
64 //! impl ProxyManager for MyProxyManager {
65 //!     // ... implementation ...
66 //!     fn try_connect(
67 //!         &self,
68 //!         sockaddr: SocketAddr,
69 //!         connect_id: usize,
70 //!         connect_func: Box<dyn ProxyConnect + Send>,
71 //!     ) -> bool {
72 //!         todo!()
73 //!     }
74 //!     fn remove(&self, connect_id: usize) {
75 //!         todo!()
76 //!     }
77 //! }
78 //! let (tx_cmds, _) = mpsc::channel();
79 //! // Create a LibSlirp instance with a proxy manager
80 //! let libslirp = LibSlirp::new(
81 //!     SlirpConfig::default(),
82 //!     tx_cmds,
83 //!     Some(Box::new(MyProxyManager)),
84 //! );
85 //!
86 //! // ...
87 //! ```
88 //!
89 //! This module abstracts away the complexities of interacting with the libslirp C library,
90 //! providing a more convenient and reliable way to use it in your Rust projects.
91 
92 use crate::libslirp_config;
93 use crate::libslirp_config::SlirpConfigs;
94 use crate::libslirp_sys::{
95     self, SlirpPollType, SlirpProxyConnectFunc, SlirpTimerId, SLIRP_POLL_ERR, SLIRP_POLL_HUP,
96     SLIRP_POLL_IN, SLIRP_POLL_OUT, SLIRP_POLL_PRI,
97 };
98 
99 use bytes::Bytes;
100 use core::sync::atomic::{AtomicUsize, Ordering};
101 use log::{debug, info, warn};
102 use std::cell::RefCell;
103 use std::collections::HashMap;
104 use std::ffi::{c_char, c_int, c_void, CStr};
105 use std::mem::ManuallyDrop;
106 use std::net::SocketAddr;
107 use std::rc::Rc;
108 use std::sync::mpsc;
109 use std::thread;
110 use std::time::Duration;
111 use std::time::Instant;
112 
113 type TimerOpaque = usize;
114 
115 const TIMEOUT_SECS: u64 = 1;
116 
117 struct TimerManager {
118     clock: RefCell<Instant>,
119     map: RefCell<HashMap<TimerOpaque, Timer>>,
120     timers: AtomicUsize,
121 }
122 
123 #[derive(Clone)]
124 struct Timer {
125     id: SlirpTimerId,
126     cb_opaque: usize,
127     expire_time: u64,
128 }
129 
130 /// The operations performed on the slirp thread
131 #[derive(Debug)]
132 enum SlirpCmd {
133     Input(Bytes),
134     PollResult(Vec<PollFd>, c_int),
135     TimerModified,
136     Shutdown,
137     ProxyConnect(SlirpProxyConnectFunc, usize, i32, i32),
138 }
139 
140 /// Alias for io::fd::RawFd on Unix or RawSocket on Windows (converted to i32)
141 pub type RawFd = i32;
142 
143 /// HTTP Proxy callback trait
144 pub trait ProxyManager: Send {
145     /// Attempts to establish a connection through the proxy.
try_connect( &self, sockaddr: SocketAddr, connect_id: usize, connect_func: Box<dyn ProxyConnect + Send>, ) -> bool146     fn try_connect(
147         &self,
148         sockaddr: SocketAddr,
149         connect_id: usize,
150         connect_func: Box<dyn ProxyConnect + Send>,
151     ) -> bool;
152     /// Removes a proxy connection.
remove(&self, connect_id: usize)153     fn remove(&self, connect_id: usize);
154 }
155 
156 struct CallbackContext {
157     tx_bytes: mpsc::Sender<Bytes>,
158     tx_cmds: mpsc::Sender<SlirpCmd>,
159     poll_fds: Rc<RefCell<Vec<PollFd>>>,
160     proxy_manager: Option<Box<dyn ProxyManager>>,
161     tx_proxy_bytes: Option<mpsc::Sender<Bytes>>,
162     timer_manager: Rc<TimerManager>,
163 }
164 
165 /// A poll thread request has a poll_fds and a timeout
166 type PollRequest = (Vec<PollFd>, u32);
167 
168 /// API to LibSlirp
169 
170 pub struct LibSlirp {
171     tx_cmds: mpsc::Sender<SlirpCmd>,
172 }
173 
174 impl TimerManager {
next_timer(&self) -> TimerOpaque175     fn next_timer(&self) -> TimerOpaque {
176         self.timers.fetch_add(1, Ordering::SeqCst) as TimerOpaque
177     }
178 
179     /// Finds expired Timers, clears then clones them
collect_expired(&self) -> Vec<Timer>180     fn collect_expired(&self) -> Vec<Timer> {
181         let now_ms = self.get_elapsed().as_millis() as u64;
182         self.map
183             .borrow_mut()
184             .iter_mut()
185             .filter(|(_, timer)| timer.expire_time < now_ms)
186             .map(|(_, &mut ref mut timer)| {
187                 timer.expire_time = u64::MAX;
188                 timer.clone()
189             })
190             .collect()
191     }
192 
193     /// Return the minimum duration until the next timer
min_duration(&self) -> Duration194     fn min_duration(&self) -> Duration {
195         match self.map.borrow().iter().min_by_key(|(_, timer)| timer.expire_time) {
196             Some((_, timer)) => {
197                 let now_ms = self.get_elapsed().as_millis() as u64;
198                 // Duration is >= 0
199                 Duration::from_millis(timer.expire_time.saturating_sub(now_ms))
200             }
201             None => Duration::from_millis(u64::MAX),
202         }
203     }
204 
get_elapsed(&self) -> Duration205     fn get_elapsed(&self) -> Duration {
206         self.clock.borrow().elapsed()
207     }
208 
remove(&self, timer_key: &TimerOpaque) -> Option<Timer>209     fn remove(&self, timer_key: &TimerOpaque) -> Option<Timer> {
210         self.map.borrow_mut().remove(timer_key)
211     }
212 
insert(&self, timer_key: TimerOpaque, value: Timer)213     fn insert(&self, timer_key: TimerOpaque, value: Timer) {
214         self.map.borrow_mut().insert(timer_key, value);
215     }
216 
timer_mod(&self, timer_key: &TimerOpaque, expire_time: u64)217     fn timer_mod(&self, timer_key: &TimerOpaque, expire_time: u64) {
218         if let Some(&mut ref mut timer) = self.map.borrow_mut().get_mut(timer_key) {
219             // expire_time is >= 0
220             timer.expire_time = expire_time;
221         } else {
222             warn!("Unknown timer {timer_key}");
223         }
224     }
225 }
226 
227 impl LibSlirp {
228     /// Creates a new `LibSlirp` instance.
new( config: libslirp_config::SlirpConfig, tx_bytes: mpsc::Sender<Bytes>, proxy_manager: Option<Box<dyn ProxyManager>>, tx_proxy_bytes: Option<mpsc::Sender<Bytes>>, ) -> LibSlirp229     pub fn new(
230         config: libslirp_config::SlirpConfig,
231         tx_bytes: mpsc::Sender<Bytes>,
232         proxy_manager: Option<Box<dyn ProxyManager>>,
233         tx_proxy_bytes: Option<mpsc::Sender<Bytes>>,
234     ) -> LibSlirp {
235         let (tx_cmds, rx_cmds) = mpsc::channel::<SlirpCmd>();
236         let (tx_poll, rx_poll) = mpsc::channel::<PollRequest>();
237 
238         // Create channels for polling thread and launch
239         let tx_cmds_poll = tx_cmds.clone();
240         if let Err(e) = thread::Builder::new()
241             .name("slirp_poll".to_string())
242             .spawn(move || slirp_poll_thread(rx_poll, tx_cmds_poll))
243         {
244             warn!("Failed to start slirp poll thread: {}", e);
245         }
246 
247         let tx_cmds_slirp = tx_cmds.clone();
248         // Create channels for command processor thread and launch
249         if let Err(e) = thread::Builder::new().name("slirp".to_string()).spawn(move || {
250             slirp_thread(
251                 config,
252                 tx_bytes,
253                 tx_cmds_slirp,
254                 rx_cmds,
255                 tx_poll,
256                 proxy_manager,
257                 tx_proxy_bytes,
258             )
259         }) {
260             warn!("Failed to start slirp thread: {}", e);
261         }
262 
263         LibSlirp { tx_cmds }
264     }
265 
266     /// Shuts down the `LibSlirp` instance.
shutdown(self)267     pub fn shutdown(self) {
268         if let Err(e) = self.tx_cmds.send(SlirpCmd::Shutdown) {
269             warn!("Failed to send Shutdown cmd: {}", e);
270         }
271     }
272 
273     /// Inputs network data into the `LibSlirp` instance.
input(&self, bytes: Bytes)274     pub fn input(&self, bytes: Bytes) {
275         if let Err(e) = self.tx_cmds.send(SlirpCmd::Input(bytes)) {
276             warn!("Failed to send Input cmd: {}", e);
277         }
278     }
279 }
280 
281 struct ConnectRequest {
282     tx_cmds: mpsc::Sender<SlirpCmd>,
283     connect_func: SlirpProxyConnectFunc,
284     connect_id: usize,
285     af: i32,
286     start: Instant,
287 }
288 
289 /// Trait for handling proxy connection results.
290 pub trait ProxyConnect: Send {
291     /// Notifies libslirp about the result of a proxy connection attempt.
proxy_connect(&self, fd: i32, addr: SocketAddr)292     fn proxy_connect(&self, fd: i32, addr: SocketAddr);
293 }
294 
295 impl ProxyConnect for ConnectRequest {
proxy_connect(&self, fd: i32, addr: SocketAddr)296     fn proxy_connect(&self, fd: i32, addr: SocketAddr) {
297         // Send it to Slirp after try_connect() completed
298         let duration = self.start.elapsed().as_secs();
299         if duration > TIMEOUT_SECS {
300             warn!(
301                 "ConnectRequest for connection ID {} to {} took too long: {:?}",
302                 self.connect_id, addr, duration
303             );
304         }
305         let _ = self.tx_cmds.send(SlirpCmd::ProxyConnect(
306             self.connect_func,
307             self.connect_id,
308             fd,
309             self.af,
310         ));
311     }
312 }
313 
314 /// Converts a libslirp callback's `opaque` handle into a
315 /// `CallbackContext.`
316 ///
317 /// Wrapped in a `ManuallyDrop` because we do not want to release the
318 /// storage when the callback returns.
319 ///
320 /// # Safety
321 ///
322 /// * `opaque` must be a valid pointer to a `CallbackContext` originally passed
323 ///   to the slirp API.
callback_context_from_raw(opaque: *mut c_void) -> ManuallyDrop<Box<CallbackContext>>324 unsafe fn callback_context_from_raw(opaque: *mut c_void) -> ManuallyDrop<Box<CallbackContext>> {
325     ManuallyDrop::new(
326         // Safety:
327         //
328         // * `opaque` is a valid pointer to a `CallbackContext` originally passed
329         //    to the slirp API. The `callback_context_from_raw` function itself
330         //    is marked `unsafe` to enforce this precondition on its callers.
331         unsafe { Box::from_raw(opaque as *mut CallbackContext) },
332     )
333 }
334 
335 /// A Rust struct for the fields held by `slirp` C library through its
336 /// lifetime.
337 ///
338 /// All libslirp C calls are impl on this struct.
339 struct Slirp {
340     slirp: *mut libslirp_sys::Slirp,
341     // These fields are held by slirp C library
342     #[allow(dead_code)]
343     configs: Box<SlirpConfigs>,
344     #[allow(dead_code)]
345     callbacks: Box<libslirp_sys::SlirpCb>,
346     // Passed to API calls and then to callbacks
347     callback_context: Box<CallbackContext>,
348 }
349 
350 impl Slirp {
new(config: libslirp_config::SlirpConfig, callback_context: Box<CallbackContext>) -> Slirp351     fn new(config: libslirp_config::SlirpConfig, callback_context: Box<CallbackContext>) -> Slirp {
352         let callbacks = Box::new(libslirp_sys::SlirpCb {
353             send_packet: Some(send_packet_cb),
354             guest_error: Some(guest_error_cb),
355             clock_get_ns: Some(clock_get_ns_cb),
356             timer_new: None,
357             timer_free: Some(timer_free_cb),
358             timer_mod: Some(timer_mod_cb),
359             register_poll_fd: Some(register_poll_fd_cb),
360             unregister_poll_fd: Some(unregister_poll_fd_cb),
361             notify: Some(notify_cb),
362             init_completed: Some(init_completed_cb),
363             timer_new_opaque: Some(timer_new_opaque_cb),
364             try_connect: Some(try_connect_cb),
365             remove: Some(remove_cb),
366         });
367         let configs = Box::new(SlirpConfigs::new(&config));
368 
369         // Call libslrip "C" library to create a new instance of a slirp
370         // protocol stack.
371         //
372         // Safety: We ensure that:
373         //
374         // * `configs.c_slirp_config` is a valid pointer to the "C" config struct. It is
375         //   held by the "C" slirp library for lifetime of the slirp instance.
376         //
377         // * `callbacks` is a valid pointer to an array of callback functions.
378         //   It is held by the "C" slirp library for the lifetime of the slirp instance.
379         //
380         // * `callback_context` is an arbitrary opaque type passed back to
381         //   callback functions by libslirp.
382         let slirp = unsafe {
383             libslirp_sys::slirp_new(
384                 &configs.c_slirp_config,
385                 &*callbacks,
386                 &*callback_context as *const CallbackContext as *mut c_void,
387             )
388         };
389 
390         Slirp { slirp, configs, callbacks, callback_context }
391     }
392 
handle_timer(&self, timer: Timer)393     fn handle_timer(&self, timer: Timer) {
394         // Safety: We ensure that:
395         //
396         // * self.slirp is a valid state returned by `slirp_new()`
397         //
398         // * timer.id is a valid c_uint from "C" slirp library calling `timer_new_opaque_cb()`
399         //
400         // * timer.cb_opaque is an usize representing a pointer to callback function from
401         // "C" slirp library calling `timer_new_opaque_cb()`
402         unsafe {
403             libslirp_sys::slirp_handle_timer(self.slirp, timer.id, timer.cb_opaque as *mut c_void);
404         };
405     }
406 }
407 
408 impl Drop for Slirp {
409     /// # Safety
410     ///
411     /// * self.slirp is always slirp pointer initialized by slirp_new
412     ///   to the slirp API.
drop(&mut self)413     fn drop(&mut self) {
414         // Safety:
415         //
416         // * self.slirp is a slirp pointer initialized by slirp_new;
417         // it's private to the struct and is only constructed that
418         // way.
419         unsafe { libslirp_sys::slirp_cleanup(self.slirp) };
420     }
421 }
422 
slirp_thread( config: libslirp_config::SlirpConfig, tx_bytes: mpsc::Sender<Bytes>, tx_cmds: mpsc::Sender<SlirpCmd>, rx: mpsc::Receiver<SlirpCmd>, tx_poll: mpsc::Sender<PollRequest>, proxy_manager: Option<Box<dyn ProxyManager>>, tx_proxy_bytes: Option<mpsc::Sender<Bytes>>, )423 fn slirp_thread(
424     config: libslirp_config::SlirpConfig,
425     tx_bytes: mpsc::Sender<Bytes>,
426     tx_cmds: mpsc::Sender<SlirpCmd>,
427     rx: mpsc::Receiver<SlirpCmd>,
428     tx_poll: mpsc::Sender<PollRequest>,
429     proxy_manager: Option<Box<dyn ProxyManager>>,
430     tx_proxy_bytes: Option<mpsc::Sender<Bytes>>,
431 ) {
432     // Data structures wrapped in an RC are referenced through the
433     // libslirp callbacks and this code (both in the same thread).
434 
435     let timer_manager = Rc::new(TimerManager {
436         clock: RefCell::new(Instant::now()),
437         map: RefCell::new(HashMap::new()),
438         timers: AtomicUsize::new(1),
439     });
440 
441     let poll_fds = Rc::new(RefCell::new(Vec::new()));
442 
443     let callback_context = Box::new(CallbackContext {
444         tx_bytes,
445         tx_cmds,
446         poll_fds: poll_fds.clone(),
447         proxy_manager,
448         tx_proxy_bytes,
449         timer_manager: timer_manager.clone(),
450     });
451 
452     let slirp = Slirp::new(config, callback_context);
453 
454     slirp.pollfds_fill_and_send(&poll_fds, &tx_poll);
455 
456     let min_duration = timer_manager.min_duration();
457     loop {
458         let command = rx.recv_timeout(min_duration);
459         let start = Instant::now();
460 
461         let cmd_str = format!("{:?}", command);
462         match command {
463             // The dance to tell libslirp which FDs have IO ready
464             // starts with a response from a worker thread sending a
465             // PollResult, followed by pollfds_poll forwarding the FDs
466             // to libslirp, followed by giving the worker thread
467             // another set of fds to poll (and block).
468             Ok(SlirpCmd::PollResult(poll_fds_result, select_error)) => {
469                 poll_fds.borrow_mut().clone_from_slice(&poll_fds_result);
470                 slirp.pollfds_poll(select_error);
471                 slirp.pollfds_fill_and_send(&poll_fds, &tx_poll);
472             }
473             Ok(SlirpCmd::Input(bytes)) => slirp.input(&bytes),
474 
475             // A timer has been modified, new expired_time value
476             Ok(SlirpCmd::TimerModified) => continue,
477 
478             // Exit the while loop and shutdown
479             Ok(SlirpCmd::Shutdown) => break,
480 
481             Ok(SlirpCmd::ProxyConnect(func, connect_id, fd, af)) => match func {
482                 // Safety: we ensure that func (`SlirpProxyConnectFunc`)
483                 // and `connect_opaque` are valid because they originated
484                 // from the libslirp call to `try_connect_cb.`
485                 //
486                 // Parameter `fd` will be >= 0 and the descriptor for the
487                 // active socket to use, `af` will be either AF_INET or
488                 // AF_INET6. On failure `fd` will be negative.
489                 Some(func) => unsafe { func(connect_id as *mut c_void, fd as c_int, af as c_int) },
490                 None => warn!("Proxy connect function not found"),
491             },
492 
493             // Timeout... process any timers
494             Err(mpsc::RecvTimeoutError::Timeout) => continue,
495 
496             // Error
497             _ => break,
498         }
499 
500         // Explicitly store expired timers to release lock
501         let timers = timer_manager.collect_expired();
502         // Handle any expired timers' callback in the slirp thread
503         for timer in timers {
504             slirp.handle_timer(timer);
505         }
506         let duration = start.elapsed().as_secs();
507         if duration > TIMEOUT_SECS {
508             warn!("libslirp command '{cmd_str}' took too long to complete: {duration:?}");
509         }
510     }
511     // Shuts down the instance of a slirp stack and release slirp storage. No callbacks
512     // occur after this since it calls slirp_cleanup.
513     drop(slirp);
514 
515     // Shutdown slirp_poll_thread -- worst case it sends a PollResult that is ignored
516     // since this thread is no longer processing Slirp commands.
517     drop(tx_poll);
518 }
519 
520 #[derive(Clone, Debug)]
521 struct PollFd {
522     fd: c_int,
523     events: SlirpPollType,
524     revents: SlirpPollType,
525 }
526 
527 impl Slirp {
528     /// Fill the pollfds from libslirp and pass the request to the polling thread.
529     ///
530     /// This is called by the application when it is about to sleep through
531     /// poll().  *timeout is set to the amount of virtual time (in ms) that
532     /// the application intends to wait (UINT32_MAX if
533     /// infinite). slirp_pollfds_fill updates it according to e.g. TCP
534     /// timers, so the application knows it should sleep a smaller amount
535     /// of time. slirp_pollfds_fill calls add_poll for each file descriptor
536     /// that should be monitored along the sleep. The opaque pointer is
537     /// passed as such to add_poll, and add_poll returns an index.
538     ///
539     /// # Safety
540     ///
541     /// `slirp` must be a valid Slirp state returned by `slirp_new()`
pollfds_fill_and_send( &self, poll_fds: &RefCell<Vec<PollFd>>, tx: &mpsc::Sender<PollRequest>, )542     fn pollfds_fill_and_send(
543         &self,
544         poll_fds: &RefCell<Vec<PollFd>>,
545         tx: &mpsc::Sender<PollRequest>,
546     ) {
547         let mut timeout: u32 = u32::MAX;
548         poll_fds.borrow_mut().clear();
549 
550         // Safety: we ensure that:
551         //
552         // * self.slirp has a slirp pointer initialized by slirp_new,
553         // as it's private to the struct and is only constructed that way.
554         //
555         // * timeout is a valid ptr to a mutable u32.  The "C" slirp
556         // library stores into timeout.
557         //
558         // * slirp_add_poll_cb is a valid `SlirpAddPollCb` function.
559         //
560         // * self.callback_context is a CallbackContext
561         unsafe {
562             libslirp_sys::slirp_pollfds_fill(
563                 self.slirp,
564                 &mut timeout,
565                 Some(slirp_add_poll_cb),
566                 &*self.callback_context as *const CallbackContext as *mut c_void,
567             );
568         }
569         if let Err(e) = tx.send((poll_fds.borrow().to_vec(), timeout)) {
570             warn!("Failed to send poll fds: {}", e);
571         }
572     }
573 }
574 
575 /// "C" library callback that is called for each file descriptor that
576 /// should be monitored.
577 ///
578 /// # Safety
579 ///
580 /// * opaque must be a CallbackContext
slirp_add_poll_cb(fd: c_int, events: c_int, opaque: *mut c_void) -> c_int581 unsafe extern "C" fn slirp_add_poll_cb(fd: c_int, events: c_int, opaque: *mut c_void) -> c_int {
582     // Safety:
583     //
584     // * opaque is a CallbackContext
585     unsafe { callback_context_from_raw(opaque) }.add_poll(fd, events)
586 }
587 
588 impl CallbackContext {
add_poll(&mut self, fd: c_int, events: c_int) -> c_int589     fn add_poll(&mut self, fd: c_int, events: c_int) -> c_int {
590         let idx = self.poll_fds.borrow().len();
591         self.poll_fds.borrow_mut().push(PollFd { fd, events: events as SlirpPollType, revents: 0 });
592         idx as i32
593     }
594 }
595 
596 impl Slirp {
597     /// Pass the result from the polling thread back to libslirp.
598     ///
599     /// * select_error should be 1 if poll() returned an error, else 0.
pollfds_poll(&self, select_error: c_int)600     fn pollfds_poll(&self, select_error: c_int) {
601         // Call libslrip "C" library to fill poll return event information
602         // using slirp_get_revents_cb callback function.
603         //
604         // Safety: we ensure that:
605         //
606         // * self.slirp has a slirp pointer initialized by slirp_new,
607         // as it's private to the struct and is only constructed that way.
608         //
609         // * slirp_get_revents_cb is a valid `SlirpGetREventsCb` callback
610         // function.
611         //
612         // * select_error should be 1 if poll() returned an error, else 0.
613         //
614         // * self.callback_context is a CallbackContext
615         unsafe {
616             libslirp_sys::slirp_pollfds_poll(
617                 self.slirp,
618                 select_error,
619                 Some(slirp_get_revents_cb),
620                 &*self.callback_context as *const CallbackContext as *mut c_void,
621             );
622         }
623     }
624 }
625 
626 /// "C" library callback that is called on each file descriptor, giving
627 /// it the index that add_poll returned.
628 ///
629 /// # Safety
630 ///
631 /// * opaque must be a CallbackContext
slirp_get_revents_cb(idx: c_int, opaque: *mut c_void) -> c_int632 unsafe extern "C" fn slirp_get_revents_cb(idx: c_int, opaque: *mut c_void) -> c_int {
633     // Safety:
634     //
635     // * opaque is a CallbackContext
636     unsafe { callback_context_from_raw(opaque) }.get_events(idx)
637 }
638 
639 impl CallbackContext {
get_events(&self, idx: c_int) -> c_int640     fn get_events(&self, idx: c_int) -> c_int {
641         if let Some(poll_fd) = self.poll_fds.borrow().get(idx as usize) {
642             poll_fd.revents as c_int
643         } else {
644             0
645         }
646     }
647 }
648 
649 macro_rules! ternary {
650     ($cond:expr, $true_expr:expr) => {
651         if $cond != 0 {
652             $true_expr
653         } else {
654             0
655         }
656     };
657 }
658 
659 /// Worker thread that performs blocking `poll` operations on file descriptors.
660 ///
661 /// It receives polling requests from the `rx` channel, performs the `poll`, and sends the results
662 /// back to the slirp thread via the `tx` channel. This allows the slirp stack to be notified about
663 /// network events without busy waiting.
664 ///
665 /// The function handles platform-specific differences in polling mechanisms between Linux/macOS
666 /// and Windows. It also converts between Slirp's `SlirpPollType` and the OS-specific poll event types.
slirp_poll_thread(rx: mpsc::Receiver<PollRequest>, tx: mpsc::Sender<SlirpCmd>)667 fn slirp_poll_thread(rx: mpsc::Receiver<PollRequest>, tx: mpsc::Sender<SlirpCmd>) {
668     #[cfg(any(target_os = "linux", target_os = "macos"))]
669     use libc::{
670         nfds_t as OsPollFdsLenType, poll, pollfd, POLLERR as OS_POLL_ERR, POLLHUP as OS_POLL_HUP,
671         POLLIN as OS_POLL_IN, POLLNVAL as OS_POLL_NVAL, POLLOUT as OS_POLL_OUT,
672         POLLPRI as OS_POLL_PRI,
673     };
674     #[cfg(target_os = "windows")]
675     use winapi::{
676         shared::minwindef::ULONG as OsPollFdsLenType,
677         um::winsock2::{
678             WSAPoll as poll, POLLERR as OS_POLL_ERR, POLLHUP as OS_POLL_HUP,
679             POLLNVAL as OS_POLL_NVAL, POLLRDBAND as OS_POLL_PRI, POLLRDNORM as OS_POLL_IN,
680             POLLWRNORM as OS_POLL_OUT, SOCKET as FdType, WSAPOLLFD as pollfd,
681         },
682     };
683     #[cfg(any(target_os = "linux", target_os = "macos"))]
684     type FdType = c_int;
685 
686     // Convert Slirp poll (input) events to OS events definitions
687     fn to_os_events(events: SlirpPollType) -> i16 {
688         ternary!(events & SLIRP_POLL_IN, OS_POLL_IN)
689             | ternary!(events & SLIRP_POLL_OUT, OS_POLL_OUT)
690             | ternary!(events & SLIRP_POLL_PRI, OS_POLL_PRI)
691     }
692     // Convert OS (input) "events" to Slirp (input) events definitions
693     fn to_slirp_events(events: i16) -> SlirpPollType {
694         ternary!(events & OS_POLL_IN, SLIRP_POLL_IN)
695             | ternary!(events & OS_POLL_OUT, SLIRP_POLL_OUT)
696             | ternary!(events & OS_POLL_PRI, SLIRP_POLL_PRI)
697     }
698     // Convert OS (output) "revents" to Slirp revents definitions which includes ERR and HUP
699     fn to_slirp_revents(revents: i16) -> SlirpPollType {
700         to_slirp_events(revents)
701             | ternary!(revents & OS_POLL_ERR, SLIRP_POLL_ERR)
702             | ternary!(revents & OS_POLL_HUP, SLIRP_POLL_HUP)
703     }
704 
705     let mut prev_poll_fds_len = 0;
706     while let Ok((poll_fds, timeout)) = rx.recv() {
707         if poll_fds.len() != prev_poll_fds_len {
708             prev_poll_fds_len = poll_fds.len();
709             debug!("slirp_poll_thread recv poll_fds.len(): {:?}", prev_poll_fds_len);
710         }
711         // Create a c format array with the same size as poll
712         let mut os_poll_fds: Vec<pollfd> = Vec::with_capacity(poll_fds.len());
713         for fd in &poll_fds {
714             os_poll_fds.push(pollfd {
715                 fd: fd.fd as FdType,
716                 events: to_os_events(fd.events),
717                 revents: 0,
718             });
719         }
720 
721         let mut poll_result = 0;
722         // WSAPoll requires an array of one or more POLLFD structures.
723         // When nfds == 0, WSAPoll returns immediately with result -1, ignoring the timeout.
724         // (This is different from poll on Linux/macOS, which will wait for the timeout.)
725         // Therefore when nfds == 0 we will explicitly sleep for the timeout regardless of OS.
726         if os_poll_fds.is_empty() {
727             // If there are no FDs to poll, sleep for the specified timeout.
728             thread::sleep(Duration::from_millis(timeout as u64));
729         } else {
730             // Safety: we ensure that:
731             //
732             // `os_poll_fds` is a valid ptr to a vector of pollfd which
733             // the `poll` system call can write into. Note `os_poll_fds`
734             // is created and allocated above.
735             poll_result = unsafe {
736                 poll(
737                     os_poll_fds.as_mut_ptr(),
738                     os_poll_fds.len() as OsPollFdsLenType,
739                     timeout as i32,
740                 )
741             };
742         }
743         // POLLHUP and POLLERR are always allowed revents.
744         // if other events were not requested, then don't return them in the revents.
745         let allowed_revents = OS_POLL_HUP | OS_POLL_ERR;
746         let mut slirp_poll_fds: Vec<PollFd> = Vec::with_capacity(poll_fds.len());
747         for &fd in &os_poll_fds {
748             // Slrip does not handle POLLNVAL - print warning and skip
749             if fd.events & OS_POLL_NVAL != 0 {
750                 warn!("POLLNVAL event - Skip poll for fd: {:?}", fd.fd);
751                 continue;
752             }
753             slirp_poll_fds.push(PollFd {
754                 fd: fd.fd as c_int,
755                 events: to_slirp_events(fd.events),
756                 revents: to_slirp_revents(fd.revents & (fd.events | allowed_revents)),
757             });
758         }
759 
760         // 'select_error' should be 1 if poll() returned an error, else 0.
761         if let Err(e) = tx.send(SlirpCmd::PollResult(slirp_poll_fds, (poll_result < 0) as i32)) {
762             warn!("Failed to send slirp PollResult cmd: {}", e);
763         }
764     }
765 }
766 
767 impl Slirp {
768     /// Sends raw input bytes to the slirp stack.
769     ///
770     /// This function is called by the application to inject network data into the virtual network
771     /// stack. The `bytes` slice contains the raw packet data that should be processed by slirp.
input(&self, bytes: &[u8])772     fn input(&self, bytes: &[u8]) {
773         // Safety: The "C" library ensure that the memory is not
774         // referenced after the call and `bytes` does not need to remain
775         // valid after the function returns.
776         unsafe { libslirp_sys::slirp_input(self.slirp, bytes.as_ptr(), bytes.len() as i32) };
777     }
778 }
779 
780 /// Callback function invoked by the slirp stack to send an ethernet frame to the guest network.
781 ///
782 /// This function is called by the slirp stack when it has a network packet that needs to be
783 /// delivered to the guest network. The `buf` pointer points to the raw packet data, and `len`
784 /// specifies the length of the packet.
785 ///
786 /// If the guest is not ready to receive the packet, the function can drop the data. TCP will
787 /// handle retransmissions as needed.
788 ///
789 /// # Safety
790 ///
791 /// * `buf` must be a valid pointer to `len` bytes of memory.
792 /// * `len` must be greater than 0.
793 /// * `opaque` must be a valid `CallbackContext` pointer.
794 ///
795 /// # Returns
796 ///
797 /// The number of bytes sent (which should be equal to `len`).
send_packet_cb( buf: *const c_void, len: usize, opaque: *mut c_void, ) -> libslirp_sys::slirp_ssize_t798 unsafe extern "C" fn send_packet_cb(
799     buf: *const c_void,
800     len: usize,
801     opaque: *mut c_void,
802 ) -> libslirp_sys::slirp_ssize_t {
803     // Safety:
804     //
805     // * `buf` is a valid pointer to `len` bytes of memory.
806     // * `len` is greater than 0.
807     // * `opaque` is a valid `CallbackContext` pointer.
808     unsafe { callback_context_from_raw(opaque) }.send_packet(buf, len)
809 }
810 
811 impl CallbackContext {
send_packet(&self, buf: *const c_void, len: usize) -> libslirp_sys::slirp_ssize_t812     fn send_packet(&self, buf: *const c_void, len: usize) -> libslirp_sys::slirp_ssize_t {
813         // Safety: The caller ensures that `buf` is contains `len` bytes of data.
814         let c_slice = unsafe { std::slice::from_raw_parts(buf as *const u8, len) };
815         // Bytes::from(slice: &'static [u8]) creates a Bytes object without copying the data.
816         // To own its data, copy &'static [u8] to Vec<u8> before converting to Bytes.
817         let bytes = Bytes::from(c_slice.to_vec());
818         let _ = self.tx_bytes.send(bytes.clone());
819         // When HTTP Proxy is enabled, it tracks DNS packets.
820         if let Some(tx_proxy) = &self.tx_proxy_bytes {
821             let _ = tx_proxy.send(bytes);
822         }
823         len as libslirp_sys::slirp_ssize_t
824     }
825 }
826 
827 /// Callback function invoked by the slirp stack to report an error caused by guest misbehavior.
828 ///
829 /// This function is called by the slirp stack when it encounters an error condition that is
830 /// attributed to incorrect or unexpected behavior from the guest network. The `msg` parameter
831 /// contains a human-readable error message describing the issue.
832 ///
833 /// # Safety
834 ///
835 /// * `msg` must be a valid C string.
836 /// * `opaque` must be a valid `CallbackContext` pointer.
guest_error_cb(msg: *const c_char, opaque: *mut c_void)837 unsafe extern "C" fn guest_error_cb(msg: *const c_char, opaque: *mut c_void) {
838     // Safety:
839     //  * `msg` is guaranteed to be a valid C string by the caller.
840     let msg = String::from_utf8_lossy(unsafe { CStr::from_ptr(msg) }.to_bytes());
841     // Safety:
842     //  * `opaque` is guaranteed to be a valid, non-null pointer to a `CallbackContext` struct that was originally passed
843     //     to `slirp_new()` and is guaranteed to be valid for the lifetime of the Slirp instance.
844     //  * `callback_context_from_raw()` safely converts the raw `opaque` pointer back to a
845     //     `CallbackContext` reference. This is safe because the `opaque` pointer is guaranteed to be valid.
846     unsafe { callback_context_from_raw(opaque) }.guest_error(msg.to_string());
847 }
848 
849 impl CallbackContext {
guest_error(&self, msg: String)850     fn guest_error(&self, msg: String) {
851         warn!("libslirp: {msg}");
852     }
853 }
854 
855 /// Callback function invoked by the slirp stack to get the current time in nanoseconds.
856 ///
857 /// This function is called by the slirp stack to obtain the current time, which is used for
858 /// various timing-related operations within the virtual network stack.
859 ///
860 /// # Safety
861 ///
862 /// * `opaque` must be a valid `CallbackContext` pointer.
863 ///
864 /// # Returns
865 ///
866 /// The current time in nanoseconds.
clock_get_ns_cb(opaque: *mut c_void) -> i64867 unsafe extern "C" fn clock_get_ns_cb(opaque: *mut c_void) -> i64 {
868     // Safety:
869     //
870     // * `opaque` is a valid `CallbackContext` pointer.
871     //
872     unsafe { callback_context_from_raw(opaque) }.clock_get_ns()
873 }
874 
875 impl CallbackContext {
clock_get_ns(&self) -> i64876     fn clock_get_ns(&self) -> i64 {
877         self.timer_manager.get_elapsed().as_nanos() as i64
878     }
879 }
880 
881 /// Callback function invoked by the slirp stack to signal that initialization is complete.
882 ///
883 /// This function is called by the slirp stack once it has finished its initialization process
884 /// and is ready to handle network traffic.
885 ///
886 /// # Safety
887 ///
888 /// * `_slirp` is a raw pointer to the slirp instance, but it's not used in this callback.
889 /// * `opaque` must be a valid `CallbackContext` pointer.
init_completed_cb(_slirp: *mut libslirp_sys::Slirp, opaque: *mut c_void)890 unsafe extern "C" fn init_completed_cb(_slirp: *mut libslirp_sys::Slirp, opaque: *mut c_void) {
891     // Safety:
892     //
893     // * `_slirp` is a raw pointer to the slirp instance, but it's not used in this callback.
894     // * `opaque` is a valid `CallbackContext` pointer.
895     unsafe { callback_context_from_raw(opaque) }.init_completed();
896 }
897 
898 impl CallbackContext {
init_completed(&self)899     fn init_completed(&self) {
900         info!("libslirp: initialization completed.");
901     }
902 }
903 
904 /// Callback function invoked by the slirp stack to create a new timer.
905 ///
906 /// This function is called by the slirp stack when it needs to create a new timer. The `id`
907 /// parameter is a unique identifier for the timer, and `cb_opaque` is an opaque pointer that
908 /// will be passed back to the timer callback function when the timer expires.
909 ///
910 /// # Safety
911 ///
912 /// * `opaque` must be a valid `CallbackContext` pointer.
913 /// * `cb_opaque` should be a valid pointer that can be passed back to libslirp.
914 ///
915 /// # Returns
916 ///
917 /// An opaque pointer to the newly created timer.
timer_new_opaque_cb( id: SlirpTimerId, cb_opaque: *mut c_void, opaque: *mut c_void, ) -> *mut c_void918 unsafe extern "C" fn timer_new_opaque_cb(
919     id: SlirpTimerId,
920     cb_opaque: *mut c_void,
921     opaque: *mut c_void,
922 ) -> *mut c_void {
923     // Safety:
924     //  * `opaque` is a valid, non-null pointer to a `CallbackContext` struct that was originally passed
925     //     to `slirp_new()` and is guaranteed to be valid for the lifetime of the Slirp instance.
926     //  * `callback_context_from_raw()` safely converts the raw `opaque` pointer back to a
927     //     `CallbackContext` reference. This is safe because the `opaque` pointer is guaranteed to be valid.
928     unsafe { callback_context_from_raw(opaque).timer_new_opaque(id, cb_opaque) }
929 }
930 
931 impl CallbackContext {
932     /// Creates a new timer and stores it in the timer manager.
933     ///
934     /// # Safety
935     ///
936     /// * `cb_opaque` should be a valid pointer that can be passed back to libslirp.
timer_new_opaque(&self, id: SlirpTimerId, cb_opaque: *mut c_void) -> *mut c_void937     unsafe fn timer_new_opaque(&self, id: SlirpTimerId, cb_opaque: *mut c_void) -> *mut c_void {
938         let timer = self.timer_manager.next_timer();
939         self.timer_manager
940             .insert(timer, Timer { expire_time: u64::MAX, id, cb_opaque: cb_opaque as usize });
941         timer as *mut c_void
942     }
943 }
944 
945 /// Callback function invoked by the slirp stack to free a timer.
946 ///
947 /// This function is called by the slirp stack when a timer is no longer needed and should be
948 /// removed. The `timer` parameter is an opaque pointer to the timer that was created previously
949 /// using `timer_new_opaque_cb`.
950 ///
951 /// # Safety
952 ///
953 /// * `timer` must be a valid `TimerOpaque` key that was previously returned by `timer_new_opaque_cb`.
954 /// * `opaque` must be a valid `CallbackContext` pointer.
timer_free_cb(timer: *mut c_void, opaque: *mut c_void)955 unsafe extern "C" fn timer_free_cb(timer: *mut c_void, opaque: *mut c_void) {
956     // Safety:
957     //
958     // * `timer` is a valid `TimerOpaque` key that was previously returned by `timer_new_opaque_cb`.
959     // * `opaque` is a valid `CallbackContext` pointer.
960     unsafe { callback_context_from_raw(opaque) }.timer_free(timer);
961 }
962 
963 impl CallbackContext {
964     /// Removes a timer from the timer manager.
965     ///
966     /// If the timer is not found in the manager, a warning is logged.
timer_free(&self, timer: *mut c_void)967     fn timer_free(&self, timer: *mut c_void) {
968         let timer = timer as TimerOpaque;
969         if self.timer_manager.remove(&timer).is_none() {
970             warn!("Unknown timer {timer}");
971         }
972     }
973 }
974 
975 /// Callback function invoked by the slirp stack to modify an existing timer.
976 ///
977 /// This function is called by the slirp stack when it needs to change the expiration time of
978 /// an existing timer. The `timer` parameter is an opaque pointer to the timer that was created
979 /// previously using `timer_new_opaque_cb`. The `expire_time` parameter specifies the new
980 /// expiration time for the timer, in nanoseconds.
981 ///
982 /// # Safety
983 ///
984 /// * `timer` must be a valid `TimerOpaque` key that was previously returned by `timer_new_opaque_cb`.
985 /// * `opaque` must be a valid `CallbackContext` pointer.
timer_mod_cb(timer: *mut c_void, expire_time: i64, opaque: *mut c_void)986 unsafe extern "C" fn timer_mod_cb(timer: *mut c_void, expire_time: i64, opaque: *mut c_void) {
987     // Safety:
988     //
989     // * `timer` is a valid `TimerOpaque` key that was previously returned by `timer_new_opaque_cb`.
990     // * `opaque` is a valid `CallbackContext` pointer.
991     unsafe { callback_context_from_raw(opaque) }.timer_mod(timer, expire_time);
992 }
993 
994 impl CallbackContext {
995     /// Modifies the expiration time of a timer in the timer manager.
996     ///
997     /// This function updates the expiration time of the specified timer. It also sends a
998     /// notification to the slirp command thread to wake it up and reset its sleep duration,
timer_mod(&self, timer: *mut c_void, expire_time: i64)999     fn timer_mod(&self, timer: *mut c_void, expire_time: i64) {
1000         let timer_key = timer as TimerOpaque;
1001         let expire_time = std::cmp::max(expire_time, 0) as u64;
1002         self.timer_manager.timer_mod(&timer_key, expire_time);
1003         // Wake up slirp command thread to reset sleep duration
1004         let _ = self.tx_cmds.send(SlirpCmd::TimerModified);
1005     }
1006 }
1007 
register_poll_fd_cb(_fd: c_int, _opaque: *mut c_void)1008 extern "C" fn register_poll_fd_cb(_fd: c_int, _opaque: *mut c_void) {
1009     //TODO: Need implementation for Windows
1010 }
1011 
unregister_poll_fd_cb(_fd: c_int, _opaque: *mut c_void)1012 extern "C" fn unregister_poll_fd_cb(_fd: c_int, _opaque: *mut c_void) {
1013     //TODO: Need implementation for Windows
1014 }
1015 
notify_cb(_opaque: *mut c_void)1016 extern "C" fn notify_cb(_opaque: *mut c_void) {
1017     //TODO: Un-implemented
1018 }
1019 
1020 /// Callback function invoked by the slirp stack to initiate a proxy connection.
1021 ///
1022 /// This function is called by the slirp stack when it needs to establish a connection
1023 /// through a proxy. The `addr` parameter points to the address to connect to, `connect_func`
1024 /// is a callback function that should be called to notify libslirp of the connection result,
1025 /// and `connect_opaque` is an opaque pointer that will be passed back to `connect_func`.
1026 ///
1027 /// # Safety
1028 ///
1029 /// * `addr` must be a valid pointer to a `sockaddr_storage` structure.
1030 /// * `connect_func` must be a valid callback function pointer.
1031 /// * `connect_opaque` should be a valid pointer that can be passed back to libslirp.
1032 /// * `opaque` must be a valid `CallbackContext` pointer.
1033 ///
1034 /// # Returns
1035 ///
1036 /// `true` if the proxy connection request was initiated successfully, `false` otherwise.
try_connect_cb( addr: *const libslirp_sys::sockaddr_storage, connect_func: SlirpProxyConnectFunc, connect_opaque: *mut c_void, opaque: *mut c_void, ) -> bool1037 unsafe extern "C" fn try_connect_cb(
1038     addr: *const libslirp_sys::sockaddr_storage,
1039     connect_func: SlirpProxyConnectFunc,
1040     connect_opaque: *mut c_void,
1041     opaque: *mut c_void,
1042 ) -> bool {
1043     // Safety:
1044     //
1045     // * `addr` is a valid pointer to a `sockaddr_storage` structure.
1046     // * `connect_func` is a valid callback function pointer.
1047     // * `connect_opaque` is a valid pointer that can be passed back to libslirp.
1048     // * `opaque` is a valid `CallbackContext` pointer.
1049     unsafe {
1050         callback_context_from_raw(opaque).try_connect(addr, connect_func, connect_opaque as usize)
1051     }
1052 }
1053 
1054 impl CallbackContext {
1055     /// Attempts to establish a proxy connection.
1056     ///
1057     /// This function uses the `proxy_manager` to initiate a connection to the specified address.
1058     /// If the proxy manager is not available, it returns `false`.
1059     ///
1060     /// # Safety
1061     ///
1062     /// * `addr` must be a valid pointer to a `sockaddr_storage` structure.
try_connect( &self, addr: *const libslirp_sys::sockaddr_storage, connect_func: SlirpProxyConnectFunc, connect_id: usize, ) -> bool1063     unsafe fn try_connect(
1064         &self,
1065         addr: *const libslirp_sys::sockaddr_storage,
1066         connect_func: SlirpProxyConnectFunc,
1067         connect_id: usize,
1068     ) -> bool {
1069         if let Some(proxy_manager) = &self.proxy_manager {
1070             // Safety:
1071             //
1072             //  * `addr` is a valid pointer to a `sockaddr_storage` structure, as guaranteed by the caller
1073             //  * Obtaining the `ss_family` field from a valid `sockaddr_storage` struct is safe
1074             let storage = unsafe { *addr };
1075             let af = storage.ss_family as i32;
1076             let socket_addr: SocketAddr = storage.into();
1077             proxy_manager.try_connect(
1078                 socket_addr,
1079                 connect_id,
1080                 Box::new(ConnectRequest {
1081                     tx_cmds: self.tx_cmds.clone(),
1082                     connect_func,
1083                     connect_id,
1084                     af,
1085                     start: Instant::now(),
1086                 }),
1087             )
1088         } else {
1089             false
1090         }
1091     }
1092 }
1093 
1094 /// Callback function invoked by the slirp stack to remove a proxy connection.
1095 ///
1096 /// This function is called by the slirp stack when a proxy connection is no longer needed
1097 /// and should be removed. The `connect_opaque` parameter is an opaque pointer that was
1098 /// originally passed to `try_connect_cb` when the connection was initiated.
1099 ///
1100 /// # Safety
1101 ///
1102 /// * `connect_opaque` must be a valid pointer that was previously passed to `try_connect_cb`.
1103 /// * `opaque` must be a valid `CallbackContext` pointer.
remove_cb(connect_opaque: *mut c_void, opaque: *mut c_void)1104 unsafe extern "C" fn remove_cb(connect_opaque: *mut c_void, opaque: *mut c_void) {
1105     //  Safety:
1106     //
1107     // * `connect_opaque` is a valid pointer that was previously passed to `try_connect_cb`.
1108     // * `opaque` is a valid `CallbackContext` pointer.
1109     unsafe { callback_context_from_raw(opaque) }.remove(connect_opaque as usize);
1110 }
1111 
1112 impl CallbackContext {
1113     /// Removes a proxy connection from the proxy manager.
1114     ///
1115     /// This function calls the `remove` method on the `proxy_manager` to remove the
1116     /// connection associated with the given `connect_id`.
remove(&self, connect_id: usize)1117     fn remove(&self, connect_id: usize) {
1118         if let Some(proxy_connector) = &self.proxy_manager {
1119             proxy_connector.remove(connect_id);
1120         }
1121     }
1122 }
1123 
1124 #[cfg(test)]
1125 mod tests {
1126     use super::*;
1127     use std::io::{Read, Write};
1128     use std::net::{TcpListener, TcpStream};
1129     #[cfg(any(target_os = "linux", target_os = "macos"))]
1130     use std::os::unix::io::AsRawFd;
1131     #[cfg(target_os = "windows")]
1132     use std::os::windows::io::AsRawSocket;
1133 
1134     #[test]
test_version_string()1135     fn test_version_string() {
1136         // Safety:
1137         // Function returns a constant c_str
1138         let c_version_str = unsafe { CStr::from_ptr(crate::libslirp_sys::slirp_version_string()) };
1139         assert_eq!("4.7.0", c_version_str.to_str().unwrap());
1140     }
1141 
1142     // Utility function to create and launch a slirp polling thread
launch_polling_thread() -> ( mpsc::Sender<SlirpCmd>, mpsc::Receiver<SlirpCmd>, mpsc::Sender<PollRequest>, thread::JoinHandle<()>, )1143     fn launch_polling_thread() -> (
1144         mpsc::Sender<SlirpCmd>,
1145         mpsc::Receiver<SlirpCmd>,
1146         mpsc::Sender<PollRequest>,
1147         thread::JoinHandle<()>,
1148     ) {
1149         let (tx_cmds, rx_cmds) = mpsc::channel::<SlirpCmd>();
1150         let (tx_poll, rx_poll) = mpsc::channel::<PollRequest>();
1151 
1152         let tx_cmds_clone = tx_cmds.clone();
1153         let handle = thread::Builder::new()
1154             .name(format!("test_slirp_poll"))
1155             .spawn(move || slirp_poll_thread(rx_poll, tx_cmds_clone))
1156             .unwrap();
1157 
1158         (tx_cmds, rx_cmds, tx_poll, handle)
1159     }
1160 
1161     #[cfg(any(target_os = "linux", target_os = "macos"))]
to_os_fd(stream: &impl AsRawFd) -> i321162     fn to_os_fd(stream: &impl AsRawFd) -> i32 {
1163         return stream.as_raw_fd() as i32;
1164     }
1165     #[cfg(target_os = "windows")]
to_os_fd(stream: &impl AsRawSocket) -> i321166     fn to_os_fd(stream: &impl AsRawSocket) -> i32 {
1167         return stream.as_raw_socket() as i32;
1168     }
1169 
1170     // Utility function to send a poll request and receive the result
poll_and_assert_result( tx_poll: &mpsc::Sender<PollRequest>, rx_cmds: &mpsc::Receiver<SlirpCmd>, fd: i32, poll_events: SlirpPollType, expected_revents: SlirpPollType, )1171     fn poll_and_assert_result(
1172         tx_poll: &mpsc::Sender<PollRequest>,
1173         rx_cmds: &mpsc::Receiver<SlirpCmd>,
1174         fd: i32,
1175         poll_events: SlirpPollType,
1176         expected_revents: SlirpPollType,
1177     ) {
1178         assert!(
1179             tx_poll.send((vec![PollFd { fd, events: poll_events, revents: 0 }], 1000)).is_ok(),
1180             "Failed to send poll request"
1181         );
1182         if let Ok(SlirpCmd::PollResult(poll_fds, select_error)) = rx_cmds.recv() {
1183             assert_eq!(poll_fds.len(), 1, "poll_fds len is not 1.");
1184             let poll_fd = poll_fds.get(0).unwrap();
1185             assert_eq!(poll_fd.fd, fd, "poll file descriptor mismatch.");
1186             assert_eq!(poll_fd.revents, expected_revents, "poll revents mismatch.");
1187         } else {
1188             assert!(false, "Received unexpected command poll result");
1189         }
1190     }
1191 
1192     // Create and return TcpListener and TcpStream of a connected pipe
create_stream_pipe() -> (TcpListener, TcpStream)1193     fn create_stream_pipe() -> (TcpListener, TcpStream) {
1194         // Create a TcpStream pipe for testing.
1195         let listener = TcpListener::bind("127.0.0.1:0").unwrap();
1196         let addr = listener.local_addr().unwrap();
1197         let writer = TcpStream::connect(addr).unwrap();
1198         (listener, writer)
1199     }
1200 
1201     // Create and return reader and writer TcpStreams of an accepted pipe
create_accepted_stream_pipe() -> (TcpStream, TcpStream)1202     fn create_accepted_stream_pipe() -> (TcpStream, TcpStream) {
1203         // Create a TcpStream pipe
1204         let (listener, writer) = create_stream_pipe();
1205         // Accept the connection
1206         let (reader, _) = listener.accept().unwrap();
1207         (reader, writer)
1208     }
1209 
1210     // Initialize an accepted TcpStream pipe with initial data
init_pipe() -> (TcpStream, TcpStream)1211     fn init_pipe() -> (TcpStream, TcpStream) {
1212         // Create an accepted TcpStream pipe
1213         let (reader, mut writer) = create_accepted_stream_pipe();
1214         // Write initial data to pipe
1215         #[cfg(any(target_os = "linux", target_os = "macos"))]
1216         writer.write_all(&[1]).unwrap();
1217         #[cfg(target_os = "windows")]
1218         writer.write_all(b"1").unwrap();
1219 
1220         (reader, writer)
1221     }
1222 
1223     #[test]
test_slirp_poll_thread_exit()1224     fn test_slirp_poll_thread_exit() {
1225         let (_tx_cmds, _rx_cmds, tx_poll, handle) = launch_polling_thread();
1226         // Drop the sender to end the polling thread and wait for the polling thread to exit
1227         drop(tx_poll);
1228         handle.join().unwrap();
1229     }
1230 
1231     #[test]
test_poll_invalid_fd()1232     fn test_poll_invalid_fd() {
1233         // Launch the slirp polling thread.
1234         let (_tx_cmds, rx_cmds, tx_poll, handle) = launch_polling_thread();
1235 
1236         let invalid_fd = -1;
1237         // Check that the poll result indicates 0 (fd not ready).
1238         poll_and_assert_result(&tx_poll, &rx_cmds, invalid_fd, SLIRP_POLL_IN, 0);
1239 
1240         // Drop the sender to end the polling thread and wait for the polling thread to exit
1241         drop(tx_poll);
1242         handle.join().unwrap();
1243     }
1244 
1245     #[test]
test_close_fd_before_accept()1246     fn test_close_fd_before_accept() {
1247         // Launch the slirp polling thread.
1248         let (_tx_cmds, rx_cmds, tx_poll, handle) = launch_polling_thread();
1249 
1250         // Init a "broken" pipe that is closed before being accepted
1251         let (listener, writer) = create_stream_pipe();
1252 
1253         // Close the listener before accepting the connection
1254         drop(listener);
1255 
1256         // Check the expected result when file descriptor is not ready.
1257         #[cfg(target_os = "linux")]
1258         let expected_revents = SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR;
1259         // TODO: Identify way to trigger and test POLL_ERR for macOS
1260         #[cfg(target_os = "macos")]
1261         let expected_revents = SLIRP_POLL_IN | SLIRP_POLL_HUP;
1262         #[cfg(target_os = "windows")]
1263         let expected_revents = SLIRP_POLL_HUP | SLIRP_POLL_ERR;
1264         poll_and_assert_result(
1265             &tx_poll,
1266             &rx_cmds,
1267             to_os_fd(&writer),
1268             SLIRP_POLL_IN,
1269             expected_revents,
1270         );
1271 
1272         // Drop the sender to end the polling thread and wait for the polling thread to exit
1273         drop(tx_poll);
1274         handle.join().unwrap();
1275     }
1276 
1277     #[test]
test_accept_close_before_write()1278     fn test_accept_close_before_write() {
1279         // Launch the slirp polling thread.
1280         let (_tx_cmds, rx_cmds, tx_poll, handle) = launch_polling_thread();
1281         // Init a "broken" pipe that is accepted but no initial data is written
1282         let (mut reader, writer) = create_accepted_stream_pipe();
1283         let reader_fd = to_os_fd(&reader);
1284         // Close the writer end of the pipe
1285         drop(writer);
1286 
1287         // Check the expected poll result when writer is closed before data is written
1288         #[cfg(target_os = "linux")]
1289         let expected_revents = SLIRP_POLL_IN;
1290         #[cfg(target_os = "macos")]
1291         let expected_revents = SLIRP_POLL_IN | SLIRP_POLL_HUP;
1292         #[cfg(target_os = "windows")]
1293         let expected_revents = SLIRP_POLL_HUP;
1294         poll_and_assert_result(&tx_poll, &rx_cmds, reader_fd, SLIRP_POLL_IN, expected_revents);
1295 
1296         // Drop the sender to end the polling thread and wait for the polling thread to exit
1297         drop(tx_poll);
1298         handle.join().unwrap();
1299     }
1300 
1301     #[test]
test_accept_write_close()1302     fn test_accept_write_close() {
1303         // Launch the slirp polling thread.
1304         let (_tx_cmds, rx_cmds, tx_poll, handle) = launch_polling_thread();
1305         // Init a pipe for testing and get its reader file descriptor.
1306         let (mut reader, writer) = init_pipe();
1307         let reader_fd = to_os_fd(&reader);
1308 
1309         // --- Test polling for POLLIN event ---
1310 
1311         // Send a poll request and check that the poll result has POLLIN only
1312         poll_and_assert_result(&tx_poll, &rx_cmds, reader_fd, SLIRP_POLL_IN, SLIRP_POLL_IN);
1313 
1314         // Read / remove the data from the pipe.
1315         let mut buf = [0; 1];
1316         reader.read_exact(&mut buf).unwrap();
1317 
1318         // --- Test polling for no event after reading ---
1319 
1320         // Check that the poll result contains no event since there is no more data
1321         poll_and_assert_result(&tx_poll, &rx_cmds, reader_fd, SLIRP_POLL_IN, 0);
1322 
1323         // --- Test polling for POLLHUP event when writer is closed ---
1324 
1325         // Close the writer
1326         drop(writer);
1327 
1328         // Shutdown the write half of the reader
1329         reader.shutdown(std::net::Shutdown::Write).unwrap();
1330 
1331         // Check that expected poll result when writer end is dropped
1332         #[cfg(any(target_os = "linux", target_os = "macos"))]
1333         let expected_revents = SLIRP_POLL_IN | SLIRP_POLL_HUP;
1334         #[cfg(target_os = "windows")]
1335         let expected_revents = SLIRP_POLL_HUP;
1336         poll_and_assert_result(&tx_poll, &rx_cmds, reader_fd, SLIRP_POLL_IN, expected_revents);
1337 
1338         // Drop the sender to end the polling thread and wait for the polling thread to exit
1339         drop(tx_poll);
1340         handle.join().unwrap();
1341     }
1342 
1343     // TODO: Add testing for POLLNVAL case
1344 }
1345