ctru/services/soc.rs
1//! Network Socket service.
2//!
3//! By using this service the program enables the use of network sockets and utilities such as those found in `std::net`, which are completely inaccessible by default.
4//! As such, remember to hold a handle to this service handle while using any network functionality, or else the `std::net` methods will return generic OS errors.
5#![doc(alias = "socket")]
6#![doc(alias = "network")]
7
8use libc::memalign;
9use std::net::Ipv4Addr;
10use std::sync::Mutex;
11
12use crate::Error;
13use crate::error::ResultCode;
14use crate::services::ServiceReference;
15
16/// Handle to the Network Socket service.
17pub struct Soc {
18 _service_handler: ServiceReference,
19 sock_3dslink: libc::c_int,
20}
21
22static SOC_ACTIVE: Mutex<()> = Mutex::new(());
23
24impl Soc {
25 /// Initialize a new service handle using a socket buffer size of `0x100000` bytes.
26 ///
27 /// # Errors
28 ///
29 /// This function will return an error if the [`Soc`] service is already being used.
30 ///
31 /// # Example
32 ///
33 /// ```
34 /// # let _runner = test_runner::GdbRunner::default();
35 /// # use std::error::Error;
36 /// # fn main() -> Result<(), Box<dyn Error>> {
37 /// #
38 /// use ctru::services::soc::Soc;
39 ///
40 /// let soc = Soc::new()?;
41 /// #
42 /// # Ok(())
43 /// # }
44 /// ```
45 #[doc(alias = "socInit")]
46 pub fn new() -> crate::Result<Self> {
47 Self::init_with_buffer_size(0x100000)
48 }
49
50 /// Initialize a new service handle using a custom socket buffer size.
51 ///
52 /// The size should be `0x100000` bytes or greater.
53 ///
54 /// # Errors
55 ///
56 /// This function will return an error if the [`Soc`] service is already being used.
57 ///
58 /// # Example
59 ///
60 /// ```
61 /// # let _runner = test_runner::GdbRunner::default();
62 /// # use std::error::Error;
63 /// # fn main() -> Result<(), Box<dyn Error>> {
64 /// #
65 /// use ctru::services::soc::Soc;
66 ///
67 /// let soc = Soc::init_with_buffer_size(0x100000)?;
68 /// #
69 /// # Ok(())
70 /// # }
71 /// ```
72 #[doc(alias = "socInit")]
73 pub fn init_with_buffer_size(num_bytes: usize) -> crate::Result<Self> {
74 let _service_handler = ServiceReference::new(
75 &SOC_ACTIVE,
76 || {
77 let soc_mem = unsafe { memalign(0x1000, num_bytes) } as *mut u32;
78 ResultCode(unsafe { ctru_sys::socInit(soc_mem, num_bytes as u32) })?;
79
80 Ok(())
81 },
82 // `socExit` returns an error code. There is no documentantion of when errors could happen,
83 // but we wouldn't be able to handle them in the `Drop` implementation anyways.
84 // Surely nothing bad will happens :D
85 || unsafe {
86 // The socket buffer is freed automatically by `socExit`
87 let _ = ctru_sys::socExit();
88 },
89 )?;
90
91 Ok(Self {
92 _service_handler,
93 sock_3dslink: -1,
94 })
95 }
96
97 /// Returns the local IP Address of the Nintendo 3DS system.
98 ///
99 /// # Example
100 ///
101 /// ```
102 /// # let _runner = test_runner::GdbRunner::default();
103 /// # use std::error::Error;
104 /// # fn main() -> Result<(), Box<dyn Error>> {
105 /// #
106 /// use ctru::services::soc::Soc;
107 /// let soc = Soc::new()?;
108 ///
109 /// let address = soc.host_address();
110 /// #
111 /// # Ok(())
112 /// # }
113 /// ```
114 #[doc(alias = "gethostid")]
115 pub fn host_address(&self) -> Ipv4Addr {
116 let raw_id = unsafe { libc::gethostid() };
117 Ipv4Addr::from(raw_id.to_ne_bytes())
118 }
119
120 /// Redirect output streams (i.e. `stdout` and `stderr`) to the `3dslink` server.
121 ///
122 /// With this redirection it is possible to send (and view in real time) the output of `stdout` operations,
123 /// such as `println!` or `dbg!`.
124 ///
125 /// Requires `3dslink` >= 0.6.1 and `new-hbmenu` >= 2.3.0 and the use of the `--server` flag.
126 /// The `--server` flag is also availble to use via `cargo-3ds` if the requirements are met.
127 ///
128 /// # Errors
129 ///
130 /// Returns an error if a connection cannot be established to the server,
131 /// or if the output was already being redirected.
132 ///
133 /// # Example
134 ///
135 /// ```
136 /// # let _runner = test_runner::GdbRunner::default();
137 /// # use std::error::Error;
138 /// # fn main() -> Result<(), Box<dyn Error>> {
139 /// #
140 /// use ctru::services::soc::Soc;
141 /// let mut soc = Soc::new()?;
142 ///
143 /// // Redirect to the `3dslink` server that sent this program.
144 /// let address = soc.redirect_to_3dslink(true, true)?;
145 ///
146 /// println!("I'm visible from a PC!");
147 /// #
148 /// # Ok(())
149 /// # }
150 /// ```
151 #[doc(alias = "link3dsConnectToHost")]
152 pub fn redirect_to_3dslink(&mut self, stdout: bool, stderr: bool) -> crate::Result<()> {
153 if self.sock_3dslink >= 0 {
154 return Err(Error::OutputAlreadyRedirected);
155 }
156
157 if !stdout && !stderr {
158 return Ok(());
159 }
160
161 self.sock_3dslink = unsafe { ctru_sys::link3dsConnectToHost(stdout, stderr) };
162 if self.sock_3dslink < 0 {
163 Err(Error::from_errno())
164 } else {
165 Ok(())
166 }
167 }
168}
169
170impl Drop for Soc {
171 #[doc(alias = "socExit")]
172 fn drop(&mut self) {
173 if self.sock_3dslink >= 0 {
174 unsafe {
175 libc::close(self.sock_3dslink);
176 }
177 }
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn soc_duplicate() {
187 let _soc = Soc::new().unwrap();
188
189 assert!(matches!(Soc::new(), Err(Error::ServiceAlreadyActive)))
190 }
191}