xous: net: initial commit of network support
This is an initial commit of network support for Xous. On hardware, is backed by smoltcp running via a Xous server in a separate process space. This patch adds TCP and UDP client and server support as well as DNS resolution support using the dns Xous server. Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
ef4f722835
commit
aa8acc2215
9 changed files with 1493 additions and 1 deletions
|
@ -1,9 +1,15 @@
|
|||
use crate::os::xous::ffi::Connection;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
mod dns;
|
||||
pub(crate) use dns::*;
|
||||
|
||||
mod log;
|
||||
pub(crate) use log::*;
|
||||
|
||||
mod net;
|
||||
pub(crate) use net::*;
|
||||
|
||||
mod systime;
|
||||
pub(crate) use systime::*;
|
||||
|
||||
|
|
28
library/std/src/os/xous/services/dns.rs
Normal file
28
library/std/src/os/xous/services/dns.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use crate::os::xous::ffi::Connection;
|
||||
use crate::os::xous::services::connect;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
#[repr(usize)]
|
||||
pub(crate) enum DnsLendMut {
|
||||
RawLookup = 6,
|
||||
}
|
||||
|
||||
impl Into<usize> for DnsLendMut {
|
||||
fn into(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `Connection` to the DNS lookup server. This server is used for
|
||||
/// querying domain name values.
|
||||
pub(crate) fn dns_server() -> Connection {
|
||||
static DNS_CONNECTION: AtomicU32 = AtomicU32::new(0);
|
||||
let cid = DNS_CONNECTION.load(Ordering::Relaxed);
|
||||
if cid != 0 {
|
||||
return cid.into();
|
||||
}
|
||||
|
||||
let cid = connect("_DNS Resolver Middleware_").unwrap();
|
||||
DNS_CONNECTION.store(cid.into(), Ordering::Relaxed);
|
||||
cid
|
||||
}
|
95
library/std/src/os/xous/services/net.rs
Normal file
95
library/std/src/os/xous/services/net.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use crate::os::xous::ffi::Connection;
|
||||
use crate::os::xous::services::connect;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
pub(crate) enum NetBlockingScalar {
|
||||
StdGetTtlUdp(u16 /* fd */), /* 36 */
|
||||
StdSetTtlUdp(u16 /* fd */, u32 /* ttl */), /* 37 */
|
||||
StdGetTtlTcp(u16 /* fd */), /* 36 */
|
||||
StdSetTtlTcp(u16 /* fd */, u32 /* ttl */), /* 37 */
|
||||
StdGetNodelay(u16 /* fd */), /* 38 */
|
||||
StdSetNodelay(u16 /* fd */, bool), /* 39 */
|
||||
StdTcpClose(u16 /* fd */), /* 34 */
|
||||
StdUdpClose(u16 /* fd */), /* 41 */
|
||||
StdTcpStreamShutdown(u16 /* fd */, crate::net::Shutdown /* how */), /* 46 */
|
||||
}
|
||||
|
||||
pub(crate) enum NetLendMut {
|
||||
StdTcpConnect, /* 30 */
|
||||
StdTcpTx(u16 /* fd */), /* 31 */
|
||||
StdTcpPeek(u16 /* fd */, bool /* nonblocking */), /* 32 */
|
||||
StdTcpRx(u16 /* fd */, bool /* nonblocking */), /* 33 */
|
||||
StdGetAddress(u16 /* fd */), /* 35 */
|
||||
StdUdpBind, /* 40 */
|
||||
StdUdpRx(u16 /* fd */), /* 42 */
|
||||
StdUdpTx(u16 /* fd */), /* 43 */
|
||||
StdTcpListen, /* 44 */
|
||||
StdTcpAccept(u16 /* fd */), /* 45 */
|
||||
}
|
||||
|
||||
impl Into<usize> for NetLendMut {
|
||||
fn into(self) -> usize {
|
||||
match self {
|
||||
NetLendMut::StdTcpConnect => 30,
|
||||
NetLendMut::StdTcpTx(fd) => 31 | ((fd as usize) << 16),
|
||||
NetLendMut::StdTcpPeek(fd, blocking) => {
|
||||
32 | ((fd as usize) << 16) | if blocking { 0x8000 } else { 0 }
|
||||
}
|
||||
NetLendMut::StdTcpRx(fd, blocking) => {
|
||||
33 | ((fd as usize) << 16) | if blocking { 0x8000 } else { 0 }
|
||||
}
|
||||
NetLendMut::StdGetAddress(fd) => 35 | ((fd as usize) << 16),
|
||||
NetLendMut::StdUdpBind => 40,
|
||||
NetLendMut::StdUdpRx(fd) => 42 | ((fd as usize) << 16),
|
||||
NetLendMut::StdUdpTx(fd) => 43 | ((fd as usize) << 16),
|
||||
NetLendMut::StdTcpListen => 44,
|
||||
NetLendMut::StdTcpAccept(fd) => 45 | ((fd as usize) << 16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<[usize; 5]> for NetBlockingScalar {
|
||||
fn into(self) -> [usize; 5] {
|
||||
match self {
|
||||
NetBlockingScalar::StdGetTtlTcp(fd) => [36 | ((fd as usize) << 16), 0, 0, 0, 0],
|
||||
NetBlockingScalar::StdGetTtlUdp(fd) => [36 | ((fd as usize) << 16), 0, 0, 0, 1],
|
||||
NetBlockingScalar::StdSetTtlTcp(fd, ttl) => {
|
||||
[37 | ((fd as usize) << 16), ttl as _, 0, 0, 0]
|
||||
}
|
||||
NetBlockingScalar::StdSetTtlUdp(fd, ttl) => {
|
||||
[37 | ((fd as usize) << 16), ttl as _, 0, 0, 1]
|
||||
}
|
||||
NetBlockingScalar::StdGetNodelay(fd) => [38 | ((fd as usize) << 16), 0, 0, 0, 0],
|
||||
NetBlockingScalar::StdSetNodelay(fd, enabled) => {
|
||||
[39 | ((fd as usize) << 16), if enabled { 1 } else { 0 }, 0, 0, 1]
|
||||
}
|
||||
NetBlockingScalar::StdTcpClose(fd) => [34 | ((fd as usize) << 16), 0, 0, 0, 0],
|
||||
NetBlockingScalar::StdUdpClose(fd) => [41 | ((fd as usize) << 16), 0, 0, 0, 0],
|
||||
NetBlockingScalar::StdTcpStreamShutdown(fd, how) => [
|
||||
46 | ((fd as usize) << 16),
|
||||
match how {
|
||||
crate::net::Shutdown::Read => 1,
|
||||
crate::net::Shutdown::Write => 2,
|
||||
crate::net::Shutdown::Both => 3,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `Connection` to the Network server. This server provides all
|
||||
/// OS-level networking functions.
|
||||
pub(crate) fn net_server() -> Connection {
|
||||
static NET_CONNECTION: AtomicU32 = AtomicU32::new(0);
|
||||
let cid = NET_CONNECTION.load(Ordering::Relaxed);
|
||||
if cid != 0 {
|
||||
return cid.into();
|
||||
}
|
||||
|
||||
let cid = connect("_Middleware Network Server_").unwrap();
|
||||
NET_CONNECTION.store(cid.into(), Ordering::Relaxed);
|
||||
cid
|
||||
}
|
|
@ -12,7 +12,6 @@ pub mod fs;
|
|||
#[path = "../unsupported/io.rs"]
|
||||
pub mod io;
|
||||
pub mod locks;
|
||||
#[path = "../unsupported/net.rs"]
|
||||
pub mod net;
|
||||
pub mod os;
|
||||
#[path = "../unix/os_str.rs"]
|
||||
|
|
127
library/std/src/sys/pal/xous/net/dns.rs
Normal file
127
library/std/src/sys/pal/xous/net/dns.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use crate::io;
|
||||
use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use crate::os::xous::ffi::lend_mut;
|
||||
use crate::os::xous::services::{dns_server, DnsLendMut};
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
|
||||
pub struct DnsError {
|
||||
pub code: u8,
|
||||
}
|
||||
|
||||
#[repr(C, align(4096))]
|
||||
struct LookupHostQuery([u8; 4096]);
|
||||
|
||||
pub struct LookupHost {
|
||||
data: LookupHostQuery,
|
||||
port: u16,
|
||||
offset: usize,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl LookupHost {
|
||||
pub fn port(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for LookupHost {
|
||||
type Item = SocketAddr;
|
||||
fn next(&mut self) -> Option<SocketAddr> {
|
||||
if self.offset >= self.data.0.len() {
|
||||
return None;
|
||||
}
|
||||
match self.data.0.get(self.offset) {
|
||||
Some(&4) => {
|
||||
self.offset += 1;
|
||||
if self.offset + 4 > self.data.0.len() {
|
||||
return None;
|
||||
}
|
||||
let result = Some(SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::new(
|
||||
self.data.0[self.offset],
|
||||
self.data.0[self.offset + 1],
|
||||
self.data.0[self.offset + 2],
|
||||
self.data.0[self.offset + 3],
|
||||
),
|
||||
self.port,
|
||||
)));
|
||||
self.offset += 4;
|
||||
result
|
||||
}
|
||||
Some(&6) => {
|
||||
self.offset += 1;
|
||||
if self.offset + 16 > self.data.0.len() {
|
||||
return None;
|
||||
}
|
||||
let mut new_addr = [0u8; 16];
|
||||
for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)]
|
||||
.iter()
|
||||
.zip(new_addr.iter_mut())
|
||||
{
|
||||
*octet = *src;
|
||||
}
|
||||
let result =
|
||||
Some(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.port, 0, 0)));
|
||||
self.offset += 16;
|
||||
result
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(query: &str, port: u16) -> Result<LookupHost, DnsError> {
|
||||
let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port };
|
||||
|
||||
// Copy the query into the message that gets sent to the DNS server
|
||||
for (query_byte, result_byte) in query.as_bytes().iter().zip(result.data.0.iter_mut()) {
|
||||
*result_byte = *query_byte;
|
||||
}
|
||||
|
||||
lend_mut(
|
||||
dns_server(),
|
||||
DnsLendMut::RawLookup.into(),
|
||||
&mut result.data.0,
|
||||
0,
|
||||
query.as_bytes().len(),
|
||||
)
|
||||
.unwrap();
|
||||
if result.data.0[0] != 0 {
|
||||
return Err(DnsError { code: result.data.0[1] });
|
||||
}
|
||||
assert_eq!(result.offset, 0);
|
||||
result.count = result.data.0[1] as usize;
|
||||
|
||||
// Advance the offset to the first record
|
||||
result.offset = 2;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for LookupHost {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(s: &str) -> io::Result<LookupHost> {
|
||||
macro_rules! try_opt {
|
||||
($e:expr, $msg:expr) => {
|
||||
match $e {
|
||||
Some(r) => r,
|
||||
None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &$msg)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// split the string by ':' and convert the second part to u16
|
||||
let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address");
|
||||
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
|
||||
(host, port).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(&str, u16)> for LookupHost {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(v: (&str, u16)) -> io::Result<LookupHost> {
|
||||
lookup(v.0, v.1)
|
||||
.map_err(|_e| io::const_io_error!(io::ErrorKind::InvalidInput, &"DNS failure"))
|
||||
}
|
||||
}
|
84
library/std/src/sys/pal/xous/net/mod.rs
Normal file
84
library/std/src/sys/pal/xous/net/mod.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
mod dns;
|
||||
|
||||
mod tcpstream;
|
||||
pub use tcpstream::*;
|
||||
|
||||
mod tcplistener;
|
||||
pub use tcplistener::*;
|
||||
|
||||
mod udp;
|
||||
pub use udp::*;
|
||||
|
||||
// this structure needs to be synchronized with what's in net/src/api.rs
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
enum NetError {
|
||||
// Ok = 0,
|
||||
Unaddressable = 1,
|
||||
SocketInUse = 2,
|
||||
// AccessDenied = 3,
|
||||
Invalid = 4,
|
||||
// Finished = 5,
|
||||
LibraryError = 6,
|
||||
// AlreadyUsed = 7,
|
||||
TimedOut = 8,
|
||||
WouldBlock = 9,
|
||||
}
|
||||
|
||||
#[repr(C, align(4096))]
|
||||
struct ConnectRequest {
|
||||
raw: [u8; 4096],
|
||||
}
|
||||
|
||||
#[repr(C, align(4096))]
|
||||
struct SendData {
|
||||
raw: [u8; 4096],
|
||||
}
|
||||
|
||||
#[repr(C, align(4096))]
|
||||
pub struct ReceiveData {
|
||||
raw: [u8; 4096],
|
||||
}
|
||||
|
||||
#[repr(C, align(4096))]
|
||||
pub struct GetAddress {
|
||||
raw: [u8; 4096],
|
||||
}
|
||||
|
||||
pub use dns::LookupHost;
|
||||
|
||||
#[allow(nonstandard_style)]
|
||||
pub mod netc {
|
||||
pub const AF_INET: u8 = 0;
|
||||
pub const AF_INET6: u8 = 1;
|
||||
pub type sa_family_t = u8;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct in_addr {
|
||||
pub s_addr: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct sockaddr_in {
|
||||
pub sin_family: sa_family_t,
|
||||
pub sin_port: u16,
|
||||
pub sin_addr: in_addr,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct in6_addr {
|
||||
pub s6_addr: [u8; 16],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct sockaddr_in6 {
|
||||
pub sin6_family: sa_family_t,
|
||||
pub sin6_port: u16,
|
||||
pub sin6_addr: in6_addr,
|
||||
pub sin6_flowinfo: u32,
|
||||
pub sin6_scope_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct sockaddr {}
|
||||
}
|
247
library/std/src/sys/pal/xous/net/tcplistener.rs
Normal file
247
library/std/src/sys/pal/xous/net/tcplistener.rs
Normal file
|
@ -0,0 +1,247 @@
|
|||
use super::*;
|
||||
use crate::fmt;
|
||||
use crate::io;
|
||||
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use crate::os::xous::services;
|
||||
use crate::sync::Arc;
|
||||
use core::convert::TryInto;
|
||||
use core::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering};
|
||||
|
||||
macro_rules! unimpl {
|
||||
() => {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::Unsupported,
|
||||
&"This function is not yet implemented",
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TcpListener {
|
||||
fd: Arc<AtomicU16>,
|
||||
local: SocketAddr,
|
||||
handle_count: Arc<AtomicUsize>,
|
||||
nonblocking: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl TcpListener {
|
||||
pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
|
||||
let mut addr = *socketaddr?;
|
||||
|
||||
let fd = TcpListener::bind_inner(&mut addr)?;
|
||||
return Ok(TcpListener {
|
||||
fd: Arc::new(AtomicU16::new(fd)),
|
||||
local: addr,
|
||||
handle_count: Arc::new(AtomicUsize::new(1)),
|
||||
nonblocking: Arc::new(AtomicBool::new(false)),
|
||||
});
|
||||
}
|
||||
|
||||
/// This returns the raw fd of a Listener, so that it can also be used by the
|
||||
/// accept routine to replenish the Listener object after its handle has been converted into
|
||||
/// a TcpStream object.
|
||||
fn bind_inner(addr: &mut SocketAddr) -> io::Result<u16> {
|
||||
// Construct the request
|
||||
let mut connect_request = ConnectRequest { raw: [0u8; 4096] };
|
||||
|
||||
// Serialize the StdUdpBind structure. This is done "manually" because we don't want to
|
||||
// make an auto-serdes (like bincode or rkyv) crate a dependency of Xous.
|
||||
let port_bytes = addr.port().to_le_bytes();
|
||||
connect_request.raw[0] = port_bytes[0];
|
||||
connect_request.raw[1] = port_bytes[1];
|
||||
match addr.ip() {
|
||||
IpAddr::V4(addr) => {
|
||||
connect_request.raw[2] = 4;
|
||||
for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) {
|
||||
*dest = src;
|
||||
}
|
||||
}
|
||||
IpAddr::V6(addr) => {
|
||||
connect_request.raw[2] = 6;
|
||||
for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) {
|
||||
*dest = src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Ok((_, valid)) = crate::os::xous::ffi::lend_mut(
|
||||
services::net_server(),
|
||||
services::NetLendMut::StdTcpListen.into(),
|
||||
&mut connect_request.raw,
|
||||
0,
|
||||
4096,
|
||||
) else {
|
||||
return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response"));
|
||||
};
|
||||
|
||||
// The first four bytes should be zero upon success, and will be nonzero
|
||||
// for an error.
|
||||
let response = connect_request.raw;
|
||||
if response[0] != 0 || valid == 0 {
|
||||
let errcode = response[1];
|
||||
if errcode == NetError::SocketInUse as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use"));
|
||||
} else if errcode == NetError::Invalid as u8 {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::AddrNotAvailable,
|
||||
&"Invalid address"
|
||||
));
|
||||
} else if errcode == NetError::LibraryError as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error"));
|
||||
} else {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::Other,
|
||||
&"Unable to connect or internal error"
|
||||
));
|
||||
}
|
||||
}
|
||||
let fd = response[1] as usize;
|
||||
if addr.port() == 0 {
|
||||
// oddly enough, this is a valid port and it means "give me something valid, up to you what that is"
|
||||
let assigned_port = u16::from_le_bytes(response[2..4].try_into().unwrap());
|
||||
addr.set_port(assigned_port);
|
||||
}
|
||||
// println!("TcpListening with file handle of {}\r\n", fd);
|
||||
Ok(fd.try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
|
||||
Ok(self.local)
|
||||
}
|
||||
|
||||
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
|
||||
let mut receive_request = ReceiveData { raw: [0u8; 4096] };
|
||||
|
||||
if self.nonblocking.load(Ordering::Relaxed) {
|
||||
// nonblocking
|
||||
receive_request.raw[0] = 0;
|
||||
} else {
|
||||
// blocking
|
||||
receive_request.raw[0] = 1;
|
||||
}
|
||||
|
||||
if let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut(
|
||||
services::net_server(),
|
||||
services::NetLendMut::StdTcpAccept(self.fd.load(Ordering::Relaxed)).into(),
|
||||
&mut receive_request.raw,
|
||||
0,
|
||||
0,
|
||||
) {
|
||||
if receive_request.raw[0] != 0 {
|
||||
// error case
|
||||
if receive_request.raw[1] == NetError::TimedOut as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"accept timed out",));
|
||||
} else if receive_request.raw[1] == NetError::WouldBlock as u8 {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::WouldBlock,
|
||||
&"accept would block",
|
||||
));
|
||||
} else if receive_request.raw[1] == NetError::LibraryError as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error"));
|
||||
} else {
|
||||
return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",));
|
||||
}
|
||||
} else {
|
||||
// accept successful
|
||||
let rr = &receive_request.raw;
|
||||
let stream_fd = u16::from_le_bytes(rr[1..3].try_into().unwrap());
|
||||
let port = u16::from_le_bytes(rr[20..22].try_into().unwrap());
|
||||
let addr = if rr[3] == 4 {
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(rr[4], rr[5], rr[6], rr[7])), port)
|
||||
} else if rr[3] == 6 {
|
||||
SocketAddr::new(
|
||||
IpAddr::V6(Ipv6Addr::new(
|
||||
u16::from_be_bytes(rr[4..6].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[6..8].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[8..10].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[10..12].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[12..14].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[14..16].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[16..18].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[18..20].try_into().unwrap()),
|
||||
)),
|
||||
port,
|
||||
)
|
||||
} else {
|
||||
return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",));
|
||||
};
|
||||
|
||||
// replenish the listener
|
||||
let mut local_copy = self.local.clone(); // port is non-0 by this time, but the method signature needs a mut
|
||||
let new_fd = TcpListener::bind_inner(&mut local_copy)?;
|
||||
self.fd.store(new_fd, Ordering::Relaxed);
|
||||
|
||||
// now return a stream converted from the old stream's fd
|
||||
Ok((TcpStream::from_listener(stream_fd, self.local.port(), port, addr), addr))
|
||||
}
|
||||
} else {
|
||||
Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unable to accept"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<TcpListener> {
|
||||
self.handle_count.fetch_add(1, Ordering::Relaxed);
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
|
||||
if ttl > 255 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256"));
|
||||
}
|
||||
crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdSetTtlTcp(self.fd.load(Ordering::Relaxed), ttl).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn ttl(&self) -> io::Result<u32> {
|
||||
Ok(crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdGetTtlTcp(self.fd.load(Ordering::Relaxed)).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|res| res[0] as _)?)
|
||||
}
|
||||
|
||||
pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn only_v6(&self) -> io::Result<bool> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
// this call doesn't have a meaning on our platform, but we can at least not panic if it's used.
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||
self.nonblocking.store(nonblocking, Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TcpListener {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "TCP listening on {:?}", self.local)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TcpListener {
|
||||
fn drop(&mut self) {
|
||||
if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
// only drop if we're the last clone
|
||||
crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
crate::os::xous::services::NetBlockingScalar::StdTcpClose(
|
||||
self.fd.load(Ordering::Relaxed),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
435
library/std/src/sys/pal/xous/net/tcpstream.rs
Normal file
435
library/std/src/sys/pal/xous/net/tcpstream.rs
Normal file
|
@ -0,0 +1,435 @@
|
|||
use super::*;
|
||||
use crate::fmt;
|
||||
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
|
||||
use crate::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use crate::os::xous::services;
|
||||
use crate::sync::Arc;
|
||||
use crate::time::Duration;
|
||||
use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
|
||||
|
||||
macro_rules! unimpl {
|
||||
() => {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::Unsupported,
|
||||
&"This function is not yet implemented",
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
enum ReadOrPeek {
|
||||
Read,
|
||||
Peek,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TcpStream {
|
||||
fd: u16,
|
||||
local_port: u16,
|
||||
remote_port: u16,
|
||||
peer_addr: SocketAddr,
|
||||
// milliseconds
|
||||
read_timeout: Arc<AtomicU32>,
|
||||
// milliseconds
|
||||
write_timeout: Arc<AtomicU32>,
|
||||
handle_count: Arc<AtomicUsize>,
|
||||
nonblocking: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
fn sockaddr_to_buf(duration: Duration, addr: &SocketAddr, buf: &mut [u8]) {
|
||||
// Construct the request.
|
||||
let port_bytes = addr.port().to_le_bytes();
|
||||
buf[0] = port_bytes[0];
|
||||
buf[1] = port_bytes[1];
|
||||
for (dest, src) in buf[2..].iter_mut().zip((duration.as_millis() as u64).to_le_bytes()) {
|
||||
*dest = src;
|
||||
}
|
||||
match addr.ip() {
|
||||
IpAddr::V4(addr) => {
|
||||
buf[10] = 4;
|
||||
for (dest, src) in buf[11..].iter_mut().zip(addr.octets()) {
|
||||
*dest = src;
|
||||
}
|
||||
}
|
||||
IpAddr::V6(addr) => {
|
||||
buf[10] = 6;
|
||||
for (dest, src) in buf[11..].iter_mut().zip(addr.octets()) {
|
||||
*dest = src;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TcpStream {
|
||||
pub(crate) fn from_listener(
|
||||
fd: u16,
|
||||
local_port: u16,
|
||||
remote_port: u16,
|
||||
peer_addr: SocketAddr,
|
||||
) -> TcpStream {
|
||||
TcpStream {
|
||||
fd,
|
||||
local_port,
|
||||
remote_port,
|
||||
peer_addr,
|
||||
read_timeout: Arc::new(AtomicU32::new(0)),
|
||||
write_timeout: Arc::new(AtomicU32::new(0)),
|
||||
handle_count: Arc::new(AtomicUsize::new(1)),
|
||||
nonblocking: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(socketaddr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
|
||||
Self::connect_timeout(socketaddr?, Duration::ZERO)
|
||||
}
|
||||
|
||||
pub fn connect_timeout(addr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> {
|
||||
let mut connect_request = ConnectRequest { raw: [0u8; 4096] };
|
||||
|
||||
// Construct the request.
|
||||
sockaddr_to_buf(duration, &addr, &mut connect_request.raw);
|
||||
|
||||
let Ok((_, valid)) = crate::os::xous::ffi::lend_mut(
|
||||
services::net_server(),
|
||||
services::NetLendMut::StdTcpConnect.into(),
|
||||
&mut connect_request.raw,
|
||||
0,
|
||||
4096,
|
||||
) else {
|
||||
return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response"));
|
||||
};
|
||||
|
||||
// The first four bytes should be zero upon success, and will be nonzero
|
||||
// for an error.
|
||||
let response = connect_request.raw;
|
||||
if response[0] != 0 || valid == 0 {
|
||||
// errcode is a u8 but stuck in a u16 where the upper byte is invalid. Mask & decode accordingly.
|
||||
let errcode = response[0];
|
||||
if errcode == NetError::SocketInUse as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use",));
|
||||
} else if errcode == NetError::Unaddressable as u8 {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::AddrNotAvailable,
|
||||
&"Invalid address",
|
||||
));
|
||||
} else {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Unable to connect or internal error",
|
||||
));
|
||||
}
|
||||
}
|
||||
let fd = u16::from_le_bytes([response[2], response[3]]);
|
||||
let local_port = u16::from_le_bytes([response[4], response[5]]);
|
||||
let remote_port = u16::from_le_bytes([response[6], response[7]]);
|
||||
// println!(
|
||||
// "Connected with local port of {}, remote port of {}, file handle of {}",
|
||||
// local_port, remote_port, fd
|
||||
// );
|
||||
Ok(TcpStream {
|
||||
fd,
|
||||
local_port,
|
||||
remote_port,
|
||||
peer_addr: *addr,
|
||||
read_timeout: Arc::new(AtomicU32::new(0)),
|
||||
write_timeout: Arc::new(AtomicU32::new(0)),
|
||||
handle_count: Arc::new(AtomicUsize::new(1)),
|
||||
nonblocking: Arc::new(AtomicBool::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
|
||||
if let Some(to) = timeout {
|
||||
if to.is_zero() {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Zero is an invalid timeout",
|
||||
));
|
||||
}
|
||||
}
|
||||
self.read_timeout.store(
|
||||
timeout.map(|t| t.as_millis().min(u32::MAX as u128) as u32).unwrap_or_default(),
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
|
||||
if let Some(to) = timeout {
|
||||
if to.is_zero() {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Zero is an invalid timeout",
|
||||
));
|
||||
}
|
||||
}
|
||||
self.write_timeout.store(
|
||||
timeout.map(|t| t.as_millis().min(u32::MAX as u128) as u32).unwrap_or_default(),
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
match self.read_timeout.load(Ordering::Relaxed) {
|
||||
0 => Ok(None),
|
||||
t => Ok(Some(Duration::from_millis(t as u64))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
match self.write_timeout.load(Ordering::Relaxed) {
|
||||
0 => Ok(None),
|
||||
t => Ok(Some(Duration::from_millis(t as u64))),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_or_peek(&self, buf: &mut [u8], op: ReadOrPeek) -> io::Result<usize> {
|
||||
let mut receive_request = ReceiveData { raw: [0u8; 4096] };
|
||||
let data_to_read = buf.len().min(receive_request.raw.len());
|
||||
|
||||
let opcode = match op {
|
||||
ReadOrPeek::Read => {
|
||||
services::NetLendMut::StdTcpRx(self.fd, self.nonblocking.load(Ordering::Relaxed))
|
||||
}
|
||||
ReadOrPeek::Peek => {
|
||||
services::NetLendMut::StdTcpPeek(self.fd, self.nonblocking.load(Ordering::Relaxed))
|
||||
}
|
||||
};
|
||||
|
||||
let Ok((offset, length)) = crate::os::xous::ffi::lend_mut(
|
||||
services::net_server(),
|
||||
opcode.into(),
|
||||
&mut receive_request.raw,
|
||||
// Reuse the `offset` as the read timeout
|
||||
self.read_timeout.load(Ordering::Relaxed) as usize,
|
||||
data_to_read,
|
||||
) else {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Library failure: wrong message type or messaging error"
|
||||
));
|
||||
};
|
||||
|
||||
if offset != 0 {
|
||||
for (dest, src) in buf.iter_mut().zip(receive_request.raw[..length].iter()) {
|
||||
*dest = *src;
|
||||
}
|
||||
Ok(length)
|
||||
} else {
|
||||
let result = receive_request.raw;
|
||||
if result[0] != 0 {
|
||||
if result[1] == 8 {
|
||||
// timed out
|
||||
return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"Timeout",));
|
||||
}
|
||||
if result[1] == 9 {
|
||||
// would block
|
||||
return Err(io::const_io_error!(io::ErrorKind::WouldBlock, &"Would block",));
|
||||
}
|
||||
}
|
||||
Err(io::const_io_error!(io::ErrorKind::Other, &"recv_slice failure"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.read_or_peek(buf, ReadOrPeek::Peek)
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.read_or_peek(buf, ReadOrPeek::Read)
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
crate::io::default_read_vectored(|b| self.read(b), bufs)
|
||||
}
|
||||
|
||||
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
crate::io::default_read_buf(|buf| self.read(buf), cursor)
|
||||
}
|
||||
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
let mut send_request = SendData { raw: [0u8; 4096] };
|
||||
for (dest, src) in send_request.raw.iter_mut().zip(buf) {
|
||||
*dest = *src;
|
||||
}
|
||||
let buf_len = send_request.raw.len().min(buf.len());
|
||||
|
||||
let (_offset, _valid) = crate::os::xous::ffi::lend_mut(
|
||||
services::net_server(),
|
||||
services::NetLendMut::StdTcpTx(self.fd).into(),
|
||||
&mut send_request.raw,
|
||||
// Reuse the offset as the timeout
|
||||
self.write_timeout.load(Ordering::Relaxed) as usize,
|
||||
buf_len,
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")))?;
|
||||
|
||||
if send_request.raw[0] != 0 {
|
||||
if send_request.raw[4] == 8 {
|
||||
// timed out
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::BrokenPipe,
|
||||
&"Timeout or connection closed",
|
||||
));
|
||||
} else if send_request.raw[4] == 9 {
|
||||
// would block
|
||||
return Err(io::const_io_error!(io::ErrorKind::WouldBlock, &"Would block",));
|
||||
} else {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Error when sending",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(u32::from_le_bytes([
|
||||
send_request.raw[4],
|
||||
send_request.raw[5],
|
||||
send_request.raw[6],
|
||||
send_request.raw[7],
|
||||
]) as usize)
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
crate::io::default_write_vectored(|b| self.write(b), bufs)
|
||||
}
|
||||
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
Ok(self.peer_addr)
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
|
||||
let mut get_addr = GetAddress { raw: [0u8; 4096] };
|
||||
|
||||
let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut(
|
||||
services::net_server(),
|
||||
services::NetLendMut::StdGetAddress(self.fd).into(),
|
||||
&mut get_addr.raw,
|
||||
0,
|
||||
0,
|
||||
) else {
|
||||
return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error"));
|
||||
};
|
||||
let mut i = get_addr.raw.iter();
|
||||
match *i.next().unwrap() {
|
||||
4 => Ok(SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::new(
|
||||
*i.next().unwrap(),
|
||||
*i.next().unwrap(),
|
||||
*i.next().unwrap(),
|
||||
*i.next().unwrap(),
|
||||
),
|
||||
self.local_port,
|
||||
))),
|
||||
6 => {
|
||||
let mut new_addr = [0u8; 16];
|
||||
for (src, octet) in i.zip(new_addr.iter_mut()) {
|
||||
*octet = *src;
|
||||
}
|
||||
Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0)))
|
||||
}
|
||||
_ => Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
|
||||
crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdTcpStreamShutdown(self.fd, how).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<TcpStream> {
|
||||
self.handle_count.fetch_add(1, Ordering::Relaxed);
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn linger(&self) -> io::Result<Option<Duration>> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn set_nodelay(&self, enabled: bool) -> io::Result<()> {
|
||||
crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdSetNodelay(self.fd, enabled).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn nodelay(&self) -> io::Result<bool> {
|
||||
Ok(crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdGetNodelay(self.fd).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|res| res[0] != 0)?)
|
||||
}
|
||||
|
||||
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
|
||||
if ttl > 255 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256"));
|
||||
}
|
||||
crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdSetTtlTcp(self.fd, ttl).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn ttl(&self) -> io::Result<u32> {
|
||||
Ok(crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdGetTtlTcp(self.fd).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|res| res[0] as _)?)
|
||||
}
|
||||
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
// this call doesn't have a meaning on our platform, but we can at least not panic if it's used.
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||
self.nonblocking.store(nonblocking, Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TcpStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"TCP connection to {:?} port {} to local port {}",
|
||||
self.peer_addr, self.remote_port, self.local_port
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TcpStream {
|
||||
fn drop(&mut self) {
|
||||
if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
// only drop if we're the last clone
|
||||
crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdTcpClose(self.fd).into(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
471
library/std/src/sys/pal/xous/net/udp.rs
Normal file
471
library/std/src/sys/pal/xous/net/udp.rs
Normal file
|
@ -0,0 +1,471 @@
|
|||
use super::*;
|
||||
use crate::cell::Cell;
|
||||
use crate::fmt;
|
||||
use crate::io;
|
||||
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use crate::os::xous::services;
|
||||
use crate::sync::Arc;
|
||||
use crate::time::Duration;
|
||||
use core::convert::TryInto;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
macro_rules! unimpl {
|
||||
() => {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::Unsupported,
|
||||
&"This function is not yet implemented",
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UdpSocket {
|
||||
fd: u16,
|
||||
local: SocketAddr,
|
||||
remote: Cell<Option<SocketAddr>>,
|
||||
// in milliseconds. The setting applies only to `recv` calls after the timeout is set.
|
||||
read_timeout: Cell<u64>,
|
||||
// in milliseconds. The setting applies only to `send` calls after the timeout is set.
|
||||
write_timeout: Cell<u64>,
|
||||
handle_count: Arc<AtomicUsize>,
|
||||
nonblocking: Cell<bool>,
|
||||
}
|
||||
|
||||
impl UdpSocket {
|
||||
pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
|
||||
let addr = socketaddr?;
|
||||
// Construct the request
|
||||
let mut connect_request = ConnectRequest { raw: [0u8; 4096] };
|
||||
|
||||
// Serialize the StdUdpBind structure. This is done "manually" because we don't want to
|
||||
// make an auto-serdes (like bincode or rkyv) crate a dependency of Xous.
|
||||
let port_bytes = addr.port().to_le_bytes();
|
||||
connect_request.raw[0] = port_bytes[0];
|
||||
connect_request.raw[1] = port_bytes[1];
|
||||
match addr.ip() {
|
||||
IpAddr::V4(addr) => {
|
||||
connect_request.raw[2] = 4;
|
||||
for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) {
|
||||
*dest = src;
|
||||
}
|
||||
}
|
||||
IpAddr::V6(addr) => {
|
||||
connect_request.raw[2] = 6;
|
||||
for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) {
|
||||
*dest = src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let response = crate::os::xous::ffi::lend_mut(
|
||||
services::net_server(),
|
||||
services::NetLendMut::StdUdpBind.into(),
|
||||
&mut connect_request.raw,
|
||||
0,
|
||||
4096,
|
||||
);
|
||||
|
||||
if let Ok((_, valid)) = response {
|
||||
// The first four bytes should be zero upon success, and will be nonzero
|
||||
// for an error.
|
||||
let response = connect_request.raw;
|
||||
if response[0] != 0 || valid == 0 {
|
||||
let errcode = response[1];
|
||||
if errcode == NetError::SocketInUse as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use"));
|
||||
} else if errcode == NetError::Invalid as u8 {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Port can't be 0 or invalid address"
|
||||
));
|
||||
} else if errcode == NetError::LibraryError as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error"));
|
||||
} else {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::Other,
|
||||
&"Unable to connect or internal error"
|
||||
));
|
||||
}
|
||||
}
|
||||
let fd = response[1] as u16;
|
||||
return Ok(UdpSocket {
|
||||
fd,
|
||||
local: *addr,
|
||||
remote: Cell::new(None),
|
||||
read_timeout: Cell::new(0),
|
||||
write_timeout: Cell::new(0),
|
||||
handle_count: Arc::new(AtomicUsize::new(1)),
|
||||
nonblocking: Cell::new(false),
|
||||
});
|
||||
}
|
||||
Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response"))
|
||||
}
|
||||
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
match self.remote.get() {
|
||||
Some(dest) => Ok(dest),
|
||||
None => Err(io::const_io_error!(io::ErrorKind::NotConnected, &"No peer specified")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
|
||||
Ok(self.local)
|
||||
}
|
||||
|
||||
fn recv_inner(&self, buf: &mut [u8], do_peek: bool) -> io::Result<(usize, SocketAddr)> {
|
||||
let mut receive_request = ReceiveData { raw: [0u8; 4096] };
|
||||
|
||||
if self.nonblocking.get() {
|
||||
// nonblocking
|
||||
receive_request.raw[0] = 0;
|
||||
} else {
|
||||
// blocking
|
||||
receive_request.raw[0] = 1;
|
||||
for (&s, d) in self
|
||||
.read_timeout
|
||||
.get()
|
||||
.to_le_bytes()
|
||||
.iter()
|
||||
.zip(receive_request.raw[1..9].iter_mut())
|
||||
{
|
||||
*d = s;
|
||||
}
|
||||
}
|
||||
if let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut(
|
||||
services::net_server(),
|
||||
services::NetLendMut::StdUdpRx(self.fd).into(),
|
||||
&mut receive_request.raw,
|
||||
if do_peek { 1 } else { 0 },
|
||||
0,
|
||||
) {
|
||||
if receive_request.raw[0] != 0 {
|
||||
// error case
|
||||
if receive_request.raw[1] == NetError::TimedOut as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"recv timed out",));
|
||||
} else if receive_request.raw[1] == NetError::WouldBlock as u8 {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::WouldBlock,
|
||||
&"recv would block",
|
||||
));
|
||||
} else if receive_request.raw[1] == NetError::LibraryError as u8 {
|
||||
return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error"));
|
||||
} else {
|
||||
return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",));
|
||||
}
|
||||
} else {
|
||||
let rr = &receive_request.raw;
|
||||
let rxlen = u16::from_le_bytes(rr[1..3].try_into().unwrap());
|
||||
let port = u16::from_le_bytes(rr[20..22].try_into().unwrap());
|
||||
let addr = if rr[3] == 4 {
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(rr[4], rr[5], rr[6], rr[7])), port)
|
||||
} else if rr[3] == 6 {
|
||||
SocketAddr::new(
|
||||
IpAddr::V6(Ipv6Addr::new(
|
||||
u16::from_be_bytes(rr[4..6].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[6..8].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[8..10].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[10..12].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[12..14].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[14..16].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[16..18].try_into().unwrap()),
|
||||
u16::from_be_bytes(rr[18..20].try_into().unwrap()),
|
||||
)),
|
||||
port,
|
||||
)
|
||||
} else {
|
||||
return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",));
|
||||
};
|
||||
for (&s, d) in rr[22..22 + rxlen as usize].iter().zip(buf.iter_mut()) {
|
||||
*d = s;
|
||||
}
|
||||
Ok((rxlen as usize, addr))
|
||||
}
|
||||
} else {
|
||||
Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unable to recv"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.recv_inner(buf, false)
|
||||
}
|
||||
|
||||
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.recv_from(buf).map(|(len, _addr)| len)
|
||||
}
|
||||
|
||||
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.recv_inner(buf, true)
|
||||
}
|
||||
|
||||
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.peek_from(buf).map(|(len, _addr)| len)
|
||||
}
|
||||
|
||||
pub fn connect(&self, maybe_addr: io::Result<&SocketAddr>) -> io::Result<()> {
|
||||
let addr = maybe_addr?;
|
||||
self.remote.set(Some(*addr));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
if let Some(addr) = self.remote.get() {
|
||||
self.send_to(buf, &addr)
|
||||
} else {
|
||||
Err(io::const_io_error!(io::ErrorKind::NotConnected, &"No remote specified"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> io::Result<usize> {
|
||||
let mut tx_req = SendData { raw: [0u8; 4096] };
|
||||
|
||||
// Construct the request.
|
||||
let port_bytes = addr.port().to_le_bytes();
|
||||
tx_req.raw[0] = port_bytes[0];
|
||||
tx_req.raw[1] = port_bytes[1];
|
||||
match addr.ip() {
|
||||
IpAddr::V4(addr) => {
|
||||
tx_req.raw[2] = 4;
|
||||
for (dest, src) in tx_req.raw[3..].iter_mut().zip(addr.octets()) {
|
||||
*dest = src;
|
||||
}
|
||||
}
|
||||
IpAddr::V6(addr) => {
|
||||
tx_req.raw[2] = 6;
|
||||
for (dest, src) in tx_req.raw[3..].iter_mut().zip(addr.octets()) {
|
||||
*dest = src;
|
||||
}
|
||||
}
|
||||
}
|
||||
let len = buf.len() as u16;
|
||||
let len_bytes = len.to_le_bytes();
|
||||
tx_req.raw[19] = len_bytes[0];
|
||||
tx_req.raw[20] = len_bytes[1];
|
||||
for (&s, d) in buf.iter().zip(tx_req.raw[21..].iter_mut()) {
|
||||
*d = s;
|
||||
}
|
||||
|
||||
// let buf = unsafe {
|
||||
// xous::MemoryRange::new(
|
||||
// &mut tx_req as *mut SendData as usize,
|
||||
// core::mem::size_of::<SendData>(),
|
||||
// )
|
||||
// .unwrap()
|
||||
// };
|
||||
|
||||
// write time-outs are implemented on the caller side. Basically, if the Net crate server
|
||||
// is too busy to take the call immediately: retry, until the timeout is reached.
|
||||
let now = crate::time::Instant::now();
|
||||
let write_timeout = if self.nonblocking.get() {
|
||||
// nonblocking
|
||||
core::time::Duration::ZERO
|
||||
} else {
|
||||
// blocking
|
||||
if self.write_timeout.get() == 0 {
|
||||
// forever
|
||||
core::time::Duration::from_millis(u64::MAX)
|
||||
} else {
|
||||
// or this amount of time
|
||||
core::time::Duration::from_millis(self.write_timeout.get())
|
||||
}
|
||||
};
|
||||
loop {
|
||||
let response = crate::os::xous::ffi::try_lend_mut(
|
||||
services::net_server(),
|
||||
services::NetLendMut::StdUdpTx(self.fd).into(),
|
||||
&mut tx_req.raw,
|
||||
0,
|
||||
4096,
|
||||
);
|
||||
match response {
|
||||
Ok((_, valid)) => {
|
||||
let response = &tx_req.raw;
|
||||
if response[0] != 0 || valid == 0 {
|
||||
let errcode = response[1];
|
||||
if errcode == NetError::SocketInUse as u8 {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::ResourceBusy,
|
||||
&"Socket in use"
|
||||
));
|
||||
} else if errcode == NetError::Invalid as u8 {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Socket not valid"
|
||||
));
|
||||
} else if errcode == NetError::LibraryError as u8 {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::Other,
|
||||
&"Library error"
|
||||
));
|
||||
} else {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::Other,
|
||||
&"Unable to connect"
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// no error
|
||||
return Ok(len as usize);
|
||||
}
|
||||
}
|
||||
Err(crate::os::xous::ffi::Error::ServerQueueFull) => {
|
||||
if now.elapsed() >= write_timeout {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::WouldBlock,
|
||||
&"Write timed out"
|
||||
));
|
||||
} else {
|
||||
// question: do we want to do something a bit more gentle than immediately retrying?
|
||||
crate::thread::yield_now();
|
||||
}
|
||||
}
|
||||
_ => return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<UdpSocket> {
|
||||
self.handle_count.fetch_add(1, Ordering::Relaxed);
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
|
||||
if let Some(d) = timeout {
|
||||
if d.is_zero() {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Zero duration is invalid"
|
||||
));
|
||||
}
|
||||
}
|
||||
self.read_timeout
|
||||
.set(timeout.map(|t| t.as_millis().min(u64::MAX as u128) as u64).unwrap_or_default());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
|
||||
if let Some(d) = timeout {
|
||||
if d.is_zero() {
|
||||
return Err(io::const_io_error!(
|
||||
io::ErrorKind::InvalidInput,
|
||||
&"Zero duration is invalid"
|
||||
));
|
||||
}
|
||||
}
|
||||
self.write_timeout
|
||||
.set(timeout.map(|t| t.as_millis().min(u64::MAX as u128) as u64).unwrap_or_default());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
match self.read_timeout.get() {
|
||||
0 => Ok(None),
|
||||
t => Ok(Some(Duration::from_millis(t as u64))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
match self.write_timeout.get() {
|
||||
0 => Ok(None),
|
||||
t => Ok(Some(Duration::from_millis(t as u64))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
|
||||
if ttl > 255 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256"));
|
||||
}
|
||||
crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdSetTtlUdp(self.fd, ttl).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn ttl(&self) -> io::Result<u32> {
|
||||
Ok(crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdGetTtlUdp(self.fd).into(),
|
||||
)
|
||||
.or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value")))
|
||||
.map(|res| res[0] as _)?)
|
||||
}
|
||||
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
// this call doesn't have a meaning on our platform, but we can at least not panic if it's used.
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||
self.nonblocking.set(nonblocking);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ------------- smoltcp base stack does not have multicast or broadcast support ---------------
|
||||
pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn broadcast(&self) -> io::Result<bool> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
|
||||
pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
|
||||
unimpl!();
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for UdpSocket {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "UDP listening on {:?} to {:?}", self.local, self.remote.get(),)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UdpSocket {
|
||||
fn drop(&mut self) {
|
||||
if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
// only drop if we're the last clone
|
||||
crate::os::xous::ffi::blocking_scalar(
|
||||
services::net_server(),
|
||||
services::NetBlockingScalar::StdUdpClose(self.fd).into(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue