Skip to content

Instantly share code, notes, and snippets.

@sajattack
Created August 13, 2021 07:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sajattack/6abab634c5f35a242dbf5e4f43b88f56 to your computer and use it in GitHub Desktop.
Save sajattack/6abab634c5f35a242dbf5e4f43b88f56 to your computer and use it in GitHub Desktop.
tls-weather
#![no_std]
#![no_main]
extern crate alloc;
use psp::sys;
use drogue_tls::blocking::*;
use drogue_network::addr::HostSocketAddr;
use drogue_network::dns::Dns;
use drogue_network::dns::AddrType;
use rand_chacha::ChaCha20Rng;
use rand_chacha::rand_core::SeedableRng;
psp::module!("tls-weather", 1, 1);
mod net;
#[no_mangle]
fn psp_main() {
psp::enable_home_button();
unsafe {
load_modules();
init();
psp::sys::sceNetApctlConnect(1);
loop {
let mut state: psp::sys::ApctlState = core::mem::zeroed();
psp::sys::sceNetApctlGetState(&mut state);
if let psp::sys::ApctlState::GotIp = state {
break;
}
psp::sys::sceKernelDelayThread(50_000);
}
}
let (sz, buf) = get_url("https://www.victoriaweather.ca/stations/UVicSci/current.xml").unwrap();
let mut text = unsafe { alloc::string::String::from_utf8_unchecked(buf[..sz].to_vec()) };
text = text.replace("\r", "");
psp::dprintln!("{}", text);
}
/// takes a url such as https://www.victoriaweather.ca/stations/UVicSci/current.xml
fn get_url(url: &str) -> Result<(usize, [u8; 8192]), ()> {
let (_protocol, rest) = url.split_once("://").unwrap();
let (hostname, path) = rest.split_once("/").unwrap();
let socket = net::Socket::open().expect("failed to open socket");
let dns_resolver = net::DnsResolver::new();
let addr = dns_resolver.gethostbyname(hostname, AddrType::IPv4).expect("dns failed");
socket.connect(HostSocketAddr::new(addr, 443)).unwrap();
let mut seed: u64 = 0;
unsafe {
sys::sceRtcGetCurrentTick(&mut seed as *mut u64);
}
let rng = ChaCha20Rng::seed_from_u64(seed);
let mut record_buffer = [0u8; 32768];
let tls_context = TlsContext::new(rng, &mut record_buffer).with_server_name(hostname);
let mut tls: TlsConnection<ChaCha20Rng, net::Socket, Aes128GcmSha256> =
TlsConnection::new(tls_context, socket);
tls.open().expect("error establishing TLS connection");
let get_req = alloc::format!("GET /{} HTTP/1.1\r\nHost: {}\r\nUser-Agent: A fucking PSP!\r\n\r\n", path, hostname);
tls.write(get_req.as_bytes()).expect("error writing data");
let mut rx_buf = [0u8; 8192];
tls.read(&mut rx_buf).expect("error reading header");
let sz = tls.read(&mut rx_buf).expect("error reading data");
Ok((sz, rx_buf))
}
unsafe fn load_modules() {
psp::sys::sceUtilityLoadNetModule(psp::sys::NetModule::NetCommon);
psp::sys::sceUtilityLoadNetModule(psp::sys::NetModule::NetInet);
}
unsafe fn init() {
psp::sys::sceNetInit(0x20000, 0x20, 0x1000, 0x20, 0x1000);
psp::sys::sceNetInetInit();
psp::sys::sceNetResolverInit();
psp::sys::sceNetApctlInit(0x1600, 42);
}
use psp::sys;
use drogue_tls::TlsError;
use drogue_network::addr::*;
use heapless::String;
use heapless::consts::U256;
use core::ffi::c_void;
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Socket(i32);
impl Socket {
pub fn open() -> Result<Socket,()> {
let sock = unsafe { sys::sceNetInetSocket(netc::AF_INET as i32, netc::SOCK_STREAM, 0) };
if sock < 0 {
Err(())
} else {
Ok(Socket(sock))
}
}
pub fn connect(&self, remote: HostSocketAddr) -> Result<(), ()> {
match remote.as_socket_addr() {
SocketAddr::V4(v4) => {
let octets = v4.ip().octets();
let sin_addr = u32::from_le_bytes(octets);
let port = v4.port().to_be();
let sockaddr_in = netc::sockaddr_in {
sin_len: core::mem::size_of::<netc::sockaddr_in>() as u8,
sin_family: netc::AF_INET,
sin_port: port,
sin_addr: netc::in_addr(sin_addr),
sin_zero: [0u8; 8],
};
let sockaddr = unsafe { core::mem::transmute::<netc::sockaddr_in, netc::sockaddr>(sockaddr_in) };
if unsafe { sys::sceNetInetConnect(self.0, &sockaddr, core::mem::size_of::<netc::sockaddr_in>() as u32) } < 0 {
unsafe { psp::dprintln!("0x{:08x}", sys::sceNetInetGetErrno()); }
Err(())
} else {
Ok(())
}
}
SocketAddr::V6(_) => {
Err(())
}
}
}
fn _read(self, buf: &mut [u8]) -> Result<usize, ()> {
let result = unsafe { sys::sceNetInetRecv(self.0, buf.as_mut_ptr() as *mut c_void, buf.len(), 0) };
if (result as i32) < 0 {
Err(())
} else {
Ok(result as usize)
}
}
fn _write(&self, buf: &[u8]) -> Result<usize, ()> {
let result = unsafe { sys::sceNetInetSend(self.0, buf.as_ptr() as *const c_void, buf.len(), 0) };
if (result as i32) < 0 {
Err(())
} else {
Ok(result as usize)
}
}
}
impl drogue_tls::traits::Read for Socket {
fn read<'m>(&'m mut self, buf: &'m mut [u8]) -> Result<usize, TlsError> {
self._read(buf).map_err(|_| TlsError::InternalError)
}
}
impl drogue_tls::traits::Write for Socket {
fn write<'m>(&'m mut self, buf: &'m [u8]) -> Result<usize, TlsError> {
self._write(buf).map_err(|_| TlsError::InternalError)
}
}
pub struct DnsResolver;
impl DnsResolver {
pub fn new() -> DnsResolver {
DnsResolver
}
}
impl drogue_network::dns::Dns for DnsResolver {
type Error = drogue_network::dns::DnsError;
fn gethostbyname(&self, hostname: &str, addr_type: drogue_network::dns::AddrType) -> Result<HostAddr, Self::Error> {
match addr_type {
drogue_network::dns::AddrType::IPv4 => {
let mut dns_buf = [0u8; 1024];
let mut rid: i32 = 0;
let hostname = alloc::string::String::from(hostname) + "\0";
let result = unsafe { sys::sceNetResolverCreate(&mut rid as *mut _ as *mut i32, &mut dns_buf as *mut _ as *mut _, dns_buf.len() as u32) };
if result < 0 {
psp::dprintln!("Create Error: {:x}", result);
return Err(drogue_network::dns::DnsError::NoSuchHost);
}
let mut in_addr: netc::in_addr = unsafe { core::mem::zeroed() };
let result = unsafe { sys::sceNetResolverStartNtoA(rid, hostname.as_bytes().as_ptr(), &mut in_addr, 5, 5) };
if result < 0 {
psp::dprintln!("NtoA Error: {:x}", result);
return Err(drogue_network::dns::DnsError::NoSuchHost);
} else {
let octets = u32::to_le_bytes(in_addr.0);
unsafe { sys::sceNetResolverDelete(rid); }
Ok(HostAddr::new(IpAddr::V4(Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])), None))
}
}
_ => Err(drogue_network::dns::DnsError::UnsupportedAddressType)
}
}
fn gethostbyaddr(&self, addr: IpAddr) -> Result<String<U256>, Self::Error> {
let mut dns_buf = [0u8; 1024];
let mut hostname_buf = heapless::Vec::<_, U256>::new();
let hostname: heapless::String<U256>;
let mut rid: i32 = 0;
if unsafe { sys::sceNetResolverCreate(&mut rid as *mut _ as *mut i32, &mut dns_buf[0] as *mut u8 as *mut _, dns_buf.len() as u32) } < 0 {
return Err(drogue_network::dns::DnsError::NoSuchHost);
}
match addr {
IpAddr::V6(_) => {
return Err(drogue_network::dns::DnsError::UnsupportedAddressType);
},
IpAddr::V4(v4) => {
let octets = v4.octets();
let ipv4addr = u32::from_le_bytes(octets);
let result = unsafe { sys::sceNetResolverStartAtoN(rid, ipv4addr as *const u32 as *const netc::in_addr, hostname_buf.as_mut_ptr(), hostname_buf.len() as u32, 5, 5) };
if result < 0 {
return Err(drogue_network::dns::DnsError::NoSuchHost);
} else {
unsafe {
hostname = String::from_utf8_unchecked(hostname_buf);
}
}
unsafe { sys::sceNetResolverDelete(rid); }
Ok(hostname)
}
}
}
}
#[allow(nonstandard_style)]
pub mod netc {
pub const AF_INET: u8 = 2;
pub const SOCK_STREAM: i32 = 1;
// pub type sa_family_t = u8;
pub use psp::sys::in_addr;
pub use psp::sys::sockaddr;
#[repr(C)]
//#[derive(Copy, Clone)]
pub struct sockaddr_in {
pub sin_len: u8,
pub sin_family: u8,
pub sin_port: u16,
pub sin_addr: in_addr,
pub sin_zero: [u8; 8]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment