#![macro_use]
extern crate libc;
use self::TransportProtocol::{Ipv4, Ipv6};
use self::TransportChannelType::{Layer3, Layer4};
use std::io;
use std::io::Error;
use std::iter::repeat;
use std::net;
use std::mem;
use std::sync::Arc;
use packet::Packet;
use packet::ip::IpNextHeaderProtocol;
use packet::ipv4::Ipv4Packet;
use packet::udp::UdpPacket;
use internal;
use util;
#[derive(Clone, Copy)]
pub enum TransportProtocol {
Ipv4(IpNextHeaderProtocol),
Ipv6(IpNextHeaderProtocol),
}
#[derive(Clone, Copy)]
pub enum TransportChannelType {
Layer4(TransportProtocol),
Layer3(IpNextHeaderProtocol),
}
pub struct TransportSender {
socket: Arc<internal::FileDesc>,
_channel_type: TransportChannelType,
}
pub struct TransportReceiver {
socket: Arc<internal::FileDesc>,
buffer: Vec<u8>,
channel_type: TransportChannelType,
}
#[cfg(windows)]
const INVALID_SOCKET: libc::SOCKET = libc::INVALID_SOCKET;
#[cfg(not(windows))]
const INVALID_SOCKET: libc::c_int = -1;
pub fn transport_channel(buffer_size: usize,
channel_type: TransportChannelType)
-> io::Result<(TransportSender, TransportReceiver)> {
use std::net;
let _ = net::lookup_host("\0");
let socket = unsafe {
match channel_type {
Layer4(Ipv4(IpNextHeaderProtocol(proto))) | Layer3(IpNextHeaderProtocol(proto)) =>
libc::socket(libc::AF_INET, libc::SOCK_RAW, proto as libc::c_int),
Layer4(Ipv6(IpNextHeaderProtocol(proto))) =>
libc::socket(libc::AF_INET6, libc::SOCK_RAW, proto as libc::c_int),
}
};
if socket != INVALID_SOCKET {
if match channel_type {
Layer3(_) | Layer4(Ipv4(_)) => true,
_ => false,
} {
let hincl: libc::c_int = match channel_type {
Layer4(..) => 0,
_ => 1,
};
let res = unsafe {
libc::setsockopt(socket,
libc::IPPROTO_IP,
libc::IP_HDRINCL,
(&hincl as *const libc::c_int) as *const libc::c_void,
mem::size_of::<libc::c_int>() as libc::socklen_t)
};
if res == -1 {
let err = Error::last_os_error();
unsafe {
internal::close(socket);
}
return Err(err);
}
}
let sock = Arc::new(internal::FileDesc { fd: socket });
let sender = TransportSender {
socket: sock.clone(),
_channel_type: channel_type,
};
let receiver = TransportReceiver {
socket: sock,
buffer: repeat(0u8).take(buffer_size).collect(),
channel_type: channel_type,
};
Ok((sender, receiver))
} else {
Err(Error::last_os_error())
}
}
impl TransportSender {
fn send<T: Packet>(&mut self, packet: T, dst: util::IpAddr) -> io::Result<usize> {
let mut caddr = unsafe { mem::zeroed() };
let sockaddr = match dst {
util::IpAddr::V4(ip_addr) =>
net::SocketAddr::V4(net::SocketAddrV4::new(ip_addr, 0)),
util::IpAddr::V6(ip_addr) =>
net::SocketAddr::V6(net::SocketAddrV6::new(ip_addr, 0, 0, 0)),
};
let slen = internal::addr_to_sockaddr(sockaddr, &mut caddr);
let caddr_ptr = (&caddr as *const libc::sockaddr_storage) as *const libc::sockaddr;
internal::send_to(self.socket.fd, packet.packet(), caddr_ptr, slen)
}
#[inline]
pub fn send_to<T: Packet>(&mut self, packet: T, destination: util::IpAddr) -> io::Result<usize> {
self.send_to_impl(packet, destination)
}
#[cfg(all(not(target_os = "freebsd"), not(target_os = "macos")))]
fn send_to_impl<T: Packet>(&mut self, packet: T, dst: util::IpAddr) -> io::Result<usize> {
self.send(packet, dst)
}
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn send_to_impl<T: Packet>(&mut self, packet: T, dst: util::IpAddr) -> io::Result<usize> {
use packet::ipv4::MutableIpv4Packet;
if match self._channel_type {
Layer3(..) => true,
_ => false,
} {
let mut mut_slice: Vec<u8> = repeat(0u8).take(packet.packet().len()).collect();
mut_slice.clone_from_slice(packet.packet());
let mut new_packet = MutableIpv4Packet::new(&mut mut_slice[..]).unwrap();
let length = new_packet.get_total_length().to_be();
new_packet.set_total_length(length);
let offset = new_packet.get_fragment_offset().to_be();
new_packet.set_fragment_offset(offset);
return self.send(new_packet, dst);
}
self.send(packet, dst)
}
}
#[macro_export]
macro_rules! transport_channel_iterator {
($ty:ident, $iter:ident, $func:ident) => (
pub struct $iter<'a> {
tr: &'a mut TransportReceiver
}
pub fn $func(tr: &mut TransportReceiver) -> $iter {
$iter {
tr: tr
}
}
impl<'a> $iter<'a> {
pub fn next(&mut self) -> io::Result<($ty, util::IpAddr)> {
let mut caddr: libc::sockaddr_storage = unsafe { mem::zeroed() };
let res = internal::recv_from(self.tr.socket.fd,
&mut self.tr.buffer[..],
&mut caddr);
let offset = match self.tr.channel_type {
Layer4(Ipv4(_)) => {
let ip_header = Ipv4Packet::new(&self.tr.buffer[..]).unwrap();
ip_header.get_header_length() as usize * 4usize
},
Layer3(_) => {
fixup_packet(&mut self.tr.buffer[..]);
0
},
_ => 0
};
return match res {
Ok(len) => {
let packet = $ty::new(&self.tr.buffer[offset..len]).unwrap();
let addr = internal::sockaddr_to_addr(
&caddr,
mem::size_of::<libc::sockaddr_storage>()
);
let ip = match addr.unwrap() {
net::SocketAddr::V4(sa) => util::IpAddr::V4(*sa.ip()),
net::SocketAddr::V6(sa) => util::IpAddr::V6(*sa.ip()),
};
Ok((packet, ip))
},
Err(e) => Err(e),
};
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn fixup_packet(buffer: &mut [u8]) {
use packet::ipv4::MutableIpv4Packet;
let buflen = buffer.len();
let mut new_packet = MutableIpv4Packet::new(buffer).unwrap();
let length = u16::from_be(new_packet.get_total_length());
new_packet.set_total_length(length);
let length = new_packet.get_total_length() as usize +
(new_packet.get_header_length() as usize * 4usize);
if length == buflen {
new_packet.set_total_length(length as u16)
}
let offset = u16::from_be(new_packet.get_fragment_offset());
new_packet.set_fragment_offset(offset);
}
#[cfg(all(not(target_os = "freebsd"), not(target_os = "macos")))]
fn fixup_packet(_buffer: &mut [u8]) {}
}
}
)
}
transport_channel_iterator!(Ipv4Packet,
Ipv4TransportChannelIterator,
ipv4_packet_iter);
transport_channel_iterator!(UdpPacket,
UdpTransportChannelIterator,
udp_packet_iter);