libtun 0.1.0

a cross-platform(macosx, linux) tunnel library
Documentation
pub use crate::packet::*;
use crate::raw::*;
use crate::*;
use ipnet::{AddrParseError, IpNet};
use smol::Task;
use std::convert::TryInto;
use std::fs::File;
use std::io::{Error, Read, Write};

use async_std::prelude::*;
use std::thread::{JoinHandle, Thread};

pub struct Systun {
  raw: RawTun,
  mtu: u32,
}

pub struct Reader {
  read_loop: JoinHandle<()>,
  raw: channel::Reader<Result<Packet>>,
  closer: channel::Closer<()>,
}

impl Reader {
  fn new(closer: channel::Closer<()>, mut fd: File, mtu: usize) -> Reader {
    let (s, r) = channel::unbound();
    let read_loop = std::thread::spawn(move || loop {
      match Self::handle_read(&mut fd, mtu) {
        Ok(pkt) => match smol::block_on(s.write(Ok(pkt))) {
          true => {}
          false => break,
        },
        Err(err) => {
          // try to notify reader
          smol::block_on(s.write(Err(err)));
          break;
        }
      };
    });
    Reader {
      read_loop,
      raw: r,
      closer,
    }
  }

  fn handle_read(fd: &mut File, mtu: usize) -> Result<Packet> {
    let mut buf = Vec::with_capacity(mtu as _);
    unsafe { buf.set_len(buf.capacity()) };
    let size = fd.read(&mut buf).context(ReadError)?;
    Ok(Packet::new(&buf[0..size]))
  }

  pub async fn read(&self) -> Option<Packet> {
    match self.raw.read().await {
      Some(result) => match result {
        Ok(pkt) => Some(pkt),
        Err(err) => {
          eprintln!("error read from tun: {}", err);
          None
        }
      },
      None => None,
    }
  }

  pub async fn read_safe(&self) -> Option<Result<Packet>> {
    self.raw.read().await
  }

  pub fn closer(&self) -> channel::Closer<Result<Packet>> {
    self.raw.closer()
  }

  pub async fn close(self) {
    self.closer.close().await
  }
}

use std::sync::Arc;

#[derive(Clone)]
pub struct Writer {
  raw: channel::Writer<Packet>,
  write_loop: Arc<JoinHandle<()>>,
  closer: channel::Closer<()>,
}

impl Writer {
  fn new(closer: channel::Closer<()>, mut fd: File) -> Writer {
    let (s, r) = channel::unbound::<Packet>();
    let write_loop = std::thread::spawn(move || {
      let mut n = 0;
      loop {
        let pkt = match smol::block_on(r.read()) {
          Some(pkt) => pkt,
          None => {
            break;
          }
        };
        let out = pkt.out();
        if let Err(write_err) = fd.write(&out) {
          eprintln!("write to tun fail: {:?}", write_err);
          break;
        }
        n += 1;
        if n > 10 {
          println!("exit");
          break;
        }
      }
    });
    Writer {
      raw: s,
      write_loop: Arc::new(write_loop),
      closer,
    }
  }

  pub async fn write(&self, pkt: Packet) -> bool {
    self.raw.write(pkt).await
  }

  fn closer(&self) -> channel::Closer<Packet> {
    self.raw.closer()
  }

  pub async fn close(self) {
    self.closer.close().await
  }
}

#[derive(Clone)]
pub struct Closer {
  raw: channel::Writer<()>,
}

impl Closer {
  pub fn new(raw: channel::Writer<()>) -> Closer {
    Closer { raw }
  }
  pub async fn close(self) {
    self.raw.close().await
  }
}

pub struct Config {}

impl Systun {
  pub async fn from_ipnet(ipnet: &IpNet) -> Result<Systun> {
    let raw = RawTun::new().await?;
    let mtu: u32 = 1500;
    let ipnet = ipnet.trunc();
    raw.init(&ipnet, mtu)?;
    Ok(Systun { raw, mtu })
  }

  pub async fn new<Addr: AsRef<str>>(addr: Addr) -> Result<Systun> {
    let ipnet = addr.as_ref().parse::<IpNet>().context(ParseAddrFail)?;
    Self::from_ipnet(&ipnet).await
  }

  pub fn split(mut self) -> (Reader, Writer) {
    let raw = &mut self.raw;

    let (close_writer, close_reader) = channel::closer();

    let raw_reader = raw.take_reader().unwrap();
    let reader = Reader::new(close_writer.clone(), raw_reader, self.mtu as _);
    let raw_writer = raw.take_writer().unwrap();
    let writer = Writer::new(close_writer, raw_writer);
    let reader_closer = reader.closer();
    let writer_closer = writer.closer();
    Task::spawn(async move {
      close_reader.read().await;
      reader_closer.close().await;
      writer_closer.close().await;
    })
    .detach();
    (reader, writer)
  }
}