redox/schemes/ipd/src/resource.rs
2016-10-20 17:49:54 -06:00

116 lines
3.4 KiB
Rust

use std::{cmp, mem};
use netutils::{n16, Ipv4Addr, Checksum, Ipv4Header, Ipv4};
use resource_scheme::Resource;
use syscall;
use syscall::error::*;
/// A IP (internet protocole) resource
pub struct IpResource {
pub link: usize,
pub data: Vec<u8>,
pub host_addr: Ipv4Addr,
pub peer_addr: Ipv4Addr,
pub proto: u8,
pub id: u16,
}
impl Resource for IpResource {
fn dup(&self) -> Result<Box<Self>> {
let link = try!(syscall::dup(self.link));
Ok(Box::new(IpResource {
link: link,
data: self.data.clone(),
host_addr: self.host_addr,
peer_addr: self.peer_addr,
proto: self.proto,
id: self.id,
}))
}
fn path(&self, buf: &mut [u8]) -> Result<usize> {
let path_string = format!("ip:{}/{:X}", self.peer_addr.to_string(), self.proto);
let path = path_string.as_bytes();
for (b, p) in buf.iter_mut().zip(path.iter()) {
*b = *p;
}
Ok(cmp::min(buf.len(), path.len()))
}
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if !self.data.is_empty() {
let mut data: Vec<u8> = Vec::new();
mem::swap(&mut self.data, &mut data);
for (b, d) in buf.iter_mut().zip(data.iter()) {
*b = *d;
}
return Ok(cmp::min(buf.len(), data.len()));
}
let mut bytes = [0; 65536];
let count = try!(syscall::read(self.link, &mut bytes));
if let Some(packet) = Ipv4::from_bytes(&bytes[..count]) {
if packet.header.proto == self.proto &&
(packet.header.dst.equals(self.host_addr) || packet.header.dst.equals(Ipv4Addr::BROADCAST)) &&
(packet.header.src.equals(self.peer_addr) || self.peer_addr.equals(Ipv4Addr::BROADCAST)) {
for (b, d) in buf.iter_mut().zip(packet.data.iter()) {
*b = *d;
}
return Ok(cmp::min(buf.len(), packet.data.len()));
}
}
Ok(0)
}
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let ip_data = Vec::from(buf);
self.id += 1;
let mut ip = Ipv4 {
header: Ipv4Header {
ver_hlen: 0x40 | (mem::size_of::<Ipv4Header>() / 4 & 0xF) as u8, // No Options
services: 0,
len: n16::new((mem::size_of::<Ipv4Header>() + ip_data.len()) as u16), // No Options
id: n16::new(self.id),
flags_fragment: n16::new(0),
ttl: 128,
proto: self.proto,
checksum: Checksum { data: 0 },
src: self.host_addr,
dst: self.peer_addr,
},
options: Vec::new(),
data: ip_data,
};
unsafe {
let header_ptr: *const Ipv4Header = &ip.header;
ip.header.checksum.data =
Checksum::compile(Checksum::sum(header_ptr as usize, mem::size_of::<Ipv4Header>()) +
Checksum::sum(ip.options.as_ptr() as usize, ip.options.len()));
}
match syscall::write(self.link, &ip.to_bytes()) {
Ok(_) => Ok(buf.len()),
Err(err) => Err(err),
}
}
fn sync(&mut self) -> Result<usize> {
syscall::fsync(self.link)
}
}
impl Drop for IpResource {
fn drop(&mut self) {
let _ = syscall::close(self.link);
}
}