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