Skip to content

Commit

Permalink
add TCP to hosted mode emulation
Browse files Browse the repository at this point in the history
Total bodge -- we lop off the TCP stack at the Xous API level, and
substitute it with a native host TCP stack.

Caveat hacker: this means that a lot of OS-specific net properties
will carry through to the hosted mode emulation. This is adequate
for "smoke testing" a build but do not be shocked, *shocked* if
there are more bugs to be found because of some weird host integration
issue.
  • Loading branch information
bunnie committed Feb 20, 2022
1 parent 535a090 commit 0dbf166
Show file tree
Hide file tree
Showing 11 changed files with 480 additions and 64 deletions.
2 changes: 2 additions & 0 deletions services/dns/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#[allow(dead_code)]
pub(crate) const SERVER_NAME_DNS: &str = "_DNS Resolver Middleware_";
use net::NetIpAddr;
use rkyv::{Archive, Deserialize, Serialize};

#[allow(dead_code)]
pub(crate) const DNS_NAME_LENGTH_LIMIT: usize = 256;
#[allow(dead_code)]
pub(crate) const DNS_PKT_MAX_LEN: usize = 512;
Expand Down
41 changes: 41 additions & 0 deletions services/dns/src/hosted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use net::NetIpAddr;
use std::net::ToSocketAddrs;
use crate::DnsResponseCode;

#[derive(Debug)]
pub struct Dns {
}
impl Dns {
pub fn new(_xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
Ok(Dns {
})
}

/// Checks first to see if the name could be just an IPv4 or IPv6 in string form,
/// then tries to pass it to the DNS resolver.
pub fn lookup(&self, name: &str) -> Result<NetIpAddr, DnsResponseCode> {
log::debug!("looking up {}", name);
match (name, 80).to_socket_addrs() { // we throw away the port because we just want the IP address...
Ok(mut iter) => {
match iter.next() {
Some(addr) => {
log::debug!("{:?}", addr);
Ok(NetIpAddr::from(addr))
},
None => {
log::debug!("name error");
Err(DnsResponseCode::NameError)
}
}
}
Err(e) => {
log::debug!("format error: {:?}", e);
Err(DnsResponseCode::FormatError)
}
}
}
pub fn flush_cache(&self) -> Result<(), xous::Error> {
log::warn!("DNS cache flush not implemented in hosted mode!");
Ok(())
}
}
63 changes: 63 additions & 0 deletions services/dns/src/hw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#![cfg_attr(target_os = "none", no_std)]
use xous::CID;
use xous_ipc::{Buffer, String};
use num_traits::ToPrimitive;

use net::NetIpAddr;
use std::net::IpAddr;

use crate::api::*;

#[derive(Debug)]
pub struct Dns {
conn: CID,
}
impl Dns {
pub fn new(xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
REFCOUNT.fetch_add(1, Ordering::Relaxed);
let conn = xns.request_connection_blocking(crate::api::SERVER_NAME_DNS).expect("Can't connect to Dns server");
Ok(Dns {
conn
})
}

/// Checks first to see if the name could be just an IPv4 or IPv6 in string form,
/// then tries to pass it to the DNS resolver.
pub fn lookup(&self, name: &str) -> Result<NetIpAddr, DnsResponseCode> {
if let Ok(simple_ip) = name.parse::<IpAddr>() {
Ok(NetIpAddr::from(simple_ip))
} else {
let alloc_name = String::<DNS_NAME_LENGTH_LIMIT>::from_str(name);
let mut buf = Buffer::into_buf(alloc_name).or(Err(DnsResponseCode::UnknownError))?;
buf.lend_mut(self.conn, Opcode::Lookup.to_u32().unwrap())
.or(Err(DnsResponseCode::UnknownError))?;
let response = buf.to_original::<DnsResponse,_>().or(Err(DnsResponseCode::UnknownError))?;
if let Some(addr) = response.addr {
Ok(addr)
} else {
Err(response.code)
}
}
}
pub fn flush_cache(&self) -> Result<(), xous::Error> {
xous::send_message(
self.conn,
xous::Message::new_scalar(Opcode::Flush.to_usize().unwrap(), 0, 0, 0, 0)
).map(|_| ())
}
}

use core::sync::atomic::{AtomicU32, Ordering};
static REFCOUNT: AtomicU32 = AtomicU32::new(0);
impl Drop for Dns {
fn drop(&mut self) {
// the connection to the server side must be reference counted, so that multiple instances of this object within
// a single process do not end up de-allocating the CID on other threads before they go out of scope.
// Note to future me: you want this. Don't get rid of it because you think, "nah, nobody will ever make more than one copy of this object".
if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
unsafe{xous::disconnect(self.conn).unwrap();}
}
// if there was object-specific state (such as a one-time use server for async callbacks, specific to the object instance),
// de-allocate those items here. They don't need a reference count because they are object-specific
}
}
68 changes: 8 additions & 60 deletions services/dns/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,64 +1,12 @@
#![cfg_attr(target_os = "none", no_std)]

pub mod api;
use api::*;
use xous::CID;
use xous_ipc::{Buffer, String};
use num_traits::ToPrimitive;

use net::NetIpAddr;
use std::net::IpAddr;

#[derive(Debug)]
pub struct Dns {
conn: CID,
}
impl Dns {
pub fn new(xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
REFCOUNT.fetch_add(1, Ordering::Relaxed);
let conn = xns.request_connection_blocking(api::SERVER_NAME_DNS).expect("Can't connect to Dns server");
Ok(Dns {
conn
})
}

/// Checks first to see if the name could be just an IPv4 or IPv6 in string form,
/// then tries to pass it to the DNS resolver.
pub fn lookup(&self, name: &str) -> Result<NetIpAddr, DnsResponseCode> {
if let Ok(simple_ip) = name.parse::<IpAddr>() {
Ok(NetIpAddr::from(simple_ip))
} else {
let alloc_name = String::<DNS_NAME_LENGTH_LIMIT>::from_str(name);
let mut buf = Buffer::into_buf(alloc_name).or(Err(DnsResponseCode::UnknownError))?;
buf.lend_mut(self.conn, Opcode::Lookup.to_u32().unwrap())
.or(Err(DnsResponseCode::UnknownError))?;
let response = buf.to_original::<DnsResponse,_>().or(Err(DnsResponseCode::UnknownError))?;
if let Some(addr) = response.addr {
Ok(addr)
} else {
Err(response.code)
}
}
}
pub fn flush_cache(&self) -> Result<(), xous::Error> {
xous::send_message(
self.conn,
xous::Message::new_scalar(Opcode::Flush.to_usize().unwrap(), 0, 0, 0, 0)
).map(|_| ())
}
}
#[cfg(any(target_os = "none", target_os = "xous"))]
mod hw;
#[cfg(any(target_os = "none", target_os = "xous"))]
pub use hw::*;

use core::sync::atomic::{AtomicU32, Ordering};
static REFCOUNT: AtomicU32 = AtomicU32::new(0);
impl Drop for Dns {
fn drop(&mut self) {
// the connection to the server side must be reference counted, so that multiple instances of this object within
// a single process do not end up de-allocating the CID on other threads before they go out of scope.
// Note to future me: you want this. Don't get rid of it because you think, "nah, nobody will ever make more than one copy of this object".
if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
unsafe{xous::disconnect(self.conn).unwrap();}
}
// if there was object-specific state (such as a one-time use server for async callbacks, specific to the object instance),
// de-allocate those items here. They don't need a reference count because they are object-specific
}
}
#[cfg(not(any(target_os = "none", target_os = "xous")))]
mod hosted;
#[cfg(not(any(target_os = "none", target_os = "xous")))]
pub use hosted::*;
1 change: 1 addition & 0 deletions services/net/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub(crate) use udp::*;
pub(crate) mod ping;
pub(crate) use ping::*;
pub(crate) mod tcp;
#[allow(unused_imports)] // needed to keep hosted mode quiet, since the Tcp implementation is a bodge
pub(crate) use tcp::*;
pub use ping::NetPingCallback;

Expand Down
18 changes: 18 additions & 0 deletions services/net/src/protocols.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
pub mod udp;
pub use udp::*;

#[cfg(any(target_os = "none", target_os = "xous"))]
pub mod dns;
#[cfg(any(target_os = "none", target_os = "xous"))]
pub use dns::*;
#[cfg(not(any(target_os = "none", target_os = "xous")))]
pub mod dns_hosted;
#[cfg(not(any(target_os = "none", target_os = "xous")))]
pub use dns_hosted::*;

pub mod ping;
pub use ping::*;

#[cfg(any(target_os = "none", target_os = "xous"))]
pub mod tcp_stream;
#[cfg(any(target_os = "none", target_os = "xous"))]
pub use tcp_stream::*;
#[cfg(any(target_os = "none", target_os = "xous"))]
pub mod tcp_listener;
#[cfg(any(target_os = "none", target_os = "xous"))]
pub use tcp_listener::*;

#[cfg(not(any(target_os = "none", target_os = "xous")))]
pub mod tcp_hosted;
#[cfg(not(any(target_os = "none", target_os = "xous")))]
pub use tcp_hosted::*;
31 changes: 31 additions & 0 deletions services/net/src/protocols/dns_hosted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use std::net::{IpAddr, Ipv4Addr};
use std::io::Result;

pub struct DnsServerManager {
}

impl DnsServerManager {
pub fn register(_xns: &xous_names::XousNames) -> Result<DnsServerManager> {
Ok(DnsServerManager {
})
}

/// Fake function that always returns true
pub fn add_server(&mut self, _addr: IpAddr) -> bool {
true
}
/// Fake function that always returns true
pub fn remove_server(&mut self, _addr: IpAddr) -> bool {
true
}
/// Fake function
pub fn clear(&mut self) {
}
/// Fake function
pub fn set_freeze(&mut self, _freeze: bool) {
}
/// Always returns 1.1.1.1
pub fn get_random(&self) -> Option<IpAddr> {
Some(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)))
}
}

0 comments on commit 0dbf166

Please sign in to comment.