extern crate libc;
extern crate os_socketaddr;
use std::io;
use std::net::{UdpSocket,ToSocketAddrs, SocketAddr, IpAddr};
use std::os::unix::io::{AsRawFd,RawFd};
use os_socketaddr::OsSocketAddr;
#[link(name="rust_udp_sas", kind="static")]
extern {
static udp_sas_IPV6_RECVPKTINFO: libc::c_int;
static udp_sas_IP_PKTINFO: libc::c_int;
fn udp_sas_recv(sock: libc::c_int,
buf: *mut u8, buf_len: libc::size_t, flags: libc::c_int,
src: *mut libc::sockaddr, src_len: libc::socklen_t,
dst: *mut libc::sockaddr, dst_len: libc::socklen_t,
) -> libc::ssize_t;
fn udp_sas_send(sock: libc::c_int,
buf: *const u8, buf_len: libc::size_t, flags: libc::c_int,
src: *const libc::sockaddr, src_len: libc::socklen_t,
dst: *const libc::sockaddr, dst_len: libc::socklen_t,
) -> libc::ssize_t;
}
use self::udp_sas_IP_PKTINFO as IP_PKTINFO;
use self::udp_sas_IPV6_RECVPKTINFO as IPV6_RECVPKTINFO;
macro_rules! try_io {
($x:expr) => {
match $x {
-1 => {return Err(io::Error::last_os_error());},
x => x
}}
}
fn getsockopt<T>(socket: RawFd, level: libc::c_int, name: libc::c_int, value: &mut T)
-> io::Result<libc::socklen_t>
{
unsafe {
let mut len = std::mem::size_of::<T>() as libc::socklen_t;
try_io!(libc::getsockopt(socket, level, name,
value as *mut T as *mut libc::c_void,
&mut len));
Ok(len)
}
}
fn setsockopt<T>(socket: RawFd, level: libc::c_int, name: libc::c_int, value: &T)
-> io::Result<()>
{
unsafe {
try_io!(libc::setsockopt(socket, level, name,
value as *const T as *const libc::c_void,
std::mem::size_of::<T>() as libc::socklen_t));
Ok(())
}
}
pub fn set_pktinfo(socket: RawFd) -> io::Result<()>
{
unsafe {
let mut domain = libc::c_int::default();
getsockopt(socket, libc::SOL_SOCKET, libc::SO_DOMAIN, &mut domain)?;
let (level, option) = match domain {
libc::AF_INET => (libc::IPPROTO_IP, IP_PKTINFO),
libc::AF_INET6 => (libc::IPPROTO_IPV6, IPV6_RECVPKTINFO),
_ => { return Err(io::Error::new(io::ErrorKind::Other, "not an inet socket")); }
};
setsockopt(socket, level, option, &(1 as libc::c_int))
}
}
pub fn recv_sas(socket: RawFd, buf: &mut [u8])
-> io::Result<(usize, Option<SocketAddr>, Option<IpAddr>)>
{
let mut src = OsSocketAddr::new();
let mut dst = OsSocketAddr::new();
let nb = {
unsafe {udp_sas_recv(socket,
buf.as_mut_ptr(), buf.len(), 0,
src.as_mut_ptr(), src.capacity() as libc::socklen_t,
dst.as_mut_ptr(), dst.capacity() as libc::socklen_t,
)}
};
if nb < 0 {
Err(io::Error::last_os_error())
} else {
Ok((nb as usize, src.into(), dst.into_addr().map(|addr| addr.ip())))
}
}
pub fn send_sas(socket: RawFd, buf: &[u8], target: Option<&SocketAddr>, local: Option<&IpAddr>)
-> io::Result<usize>
{
let src = match local {
None => OsSocketAddr::new(),
Some(ip) => SocketAddr::new(*ip, 0).into()
};
let dst : OsSocketAddr = target.map(|a|*a).into();
let nb = unsafe { udp_sas_send(socket,
buf.as_ptr(), buf.len(), 0,
src.as_ptr(), src.len() as libc::socklen_t,
dst.as_ptr(), dst.len() as libc::socklen_t)};
if nb < 0 {
Err(io::Error::last_os_error())
} else {
Ok(nb as usize)
}
}
pub trait UdpSas : Sized
{
fn bind_sas<A: ToSocketAddrs>(addr: A) -> io::Result<Self>;
fn send_sas(&self, buf: &[u8], target: &SocketAddr, local: &IpAddr) -> io::Result<usize>;
fn recv_sas(&self, buf: &mut[u8]) -> io::Result<(usize, SocketAddr, IpAddr)>;
}
impl UdpSas for UdpSocket
{
fn bind_sas<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
let sock = UdpSocket::bind(addr)?;
set_pktinfo(sock.as_raw_fd())?;
Ok(sock)
}
fn send_sas(&self, buf: &[u8], target: &SocketAddr, local: &IpAddr) -> io::Result<usize>
{
send_sas(self.as_raw_fd(), buf, Some(target), Some(local))
}
fn recv_sas(&self, buf: &mut[u8]) -> io::Result<(usize, SocketAddr, IpAddr)>
{
let (nb, src, local) = recv_sas(self.as_raw_fd(), buf)?;
match (src, local) {
(Some(src), Some(local)) => Ok((nb, src, local)),
(None, _) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"local address not available (IP_PKTINFO/IPV6_RECVPKTINFO may not be enabled on the socket)")),
(_, None) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"source address not available (maybe the socket is connected)"
)),
}
}
}