use super::super::*;
extern crate byteorder;
use self::byteorder::{ByteOrder, BigEndian, ReadBytesExt, WriteBytesExt};
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct UdpHeader {
pub source_port: u16,
pub destination_port: u16,
pub length: u16,
pub checksum: u16
}
impl UdpHeader {
pub fn without_ipv4_checksum(source_port: u16, destination_port: u16, payload_length: usize) -> Result<UdpHeader, ValueError> {
const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE;
if MAX_PAYLOAD_LENGTH < payload_length {
return Err(ValueError::UdpPayloadLengthTooLarge(payload_length));
}
Ok(UdpHeader{
source_port,
destination_port,
length: (UdpHeader::SERIALIZED_SIZE + payload_length) as u16, checksum: 0
})
}
pub fn with_ipv4_checksum(source_port: u16, destination_port: u16, ip_header: &Ipv4Header, payload: &[u8]) -> Result<UdpHeader, ValueError> {
const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE;
if MAX_PAYLOAD_LENGTH < payload.len() {
return Err(ValueError::UdpPayloadLengthTooLarge(payload.len()));
}
let mut result = UdpHeader{
source_port,
destination_port,
length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: 0
};
result.checksum = result.calc_checksum_ipv4_internal(ip_header.source, ip_header.destination, ip_header.protocol, payload);
Ok(result)
}
pub fn calc_checksum_ipv4(&self, ip_header: &Ipv4Header, payload: &[u8]) -> Result<u16, ValueError> {
self.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, ip_header.protocol, payload)
}
pub fn calc_checksum_ipv4_raw(&self, source: [u8;4], destination: [u8;4], protocol: u8, payload: &[u8]) -> Result<u16, ValueError> {
const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE;
if MAX_PAYLOAD_LENGTH < payload.len() {
return Err(ValueError::UdpPayloadLengthTooLarge(payload.len()));
}
Ok(self.calc_checksum_ipv4_internal(source, destination, protocol, payload))
}
fn calc_checksum_ipv4_internal(&self, source: [u8;4], destination: [u8;4], protocol: u8, payload: &[u8]) -> u16 {
self.calc_checksum_post_ip(u64::from( BigEndian::read_u16(&source[0..2]) ) + u64::from( BigEndian::read_u16(&source[2..4]) ) +
u64::from( BigEndian::read_u16(&destination[0..2]) ) +
u64::from( BigEndian::read_u16(&destination[2..4]) ) +
u64::from( protocol ) +
u64::from( self.length ),
payload)
}
pub fn with_ipv6_checksum(source_port: u16, destination_port: u16, ip_header: &Ipv6Header, payload: &[u8]) -> Result<UdpHeader, ValueError> {
const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE;
if MAX_PAYLOAD_LENGTH < payload.len() {
return Err(ValueError::UdpPayloadLengthTooLarge(payload.len()));
}
let mut result = UdpHeader{
source_port,
destination_port,
length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: 0
};
result.checksum = result.calc_checksum_ipv6_internal(&ip_header.source, &ip_header.destination, payload);
Ok(result)
}
pub fn calc_checksum_ipv6(&self, ip_header: &Ipv6Header, payload: &[u8]) -> Result<u16, ValueError> {
self.calc_checksum_ipv6_raw(&ip_header.source, &ip_header.destination, payload)
}
pub fn calc_checksum_ipv6_raw(&self, source: &[u8;16], destination: &[u8;16], payload: &[u8]) -> Result<u16, ValueError> {
const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE;
if MAX_PAYLOAD_LENGTH < payload.len() {
return Err(ValueError::UdpPayloadLengthTooLarge(payload.len()));
}
Ok(self.calc_checksum_ipv6_internal(source, destination, payload))
}
fn calc_checksum_ipv6_internal(&self, source: &[u8;16], destination: &[u8;16], payload: &[u8]) -> u16 {
fn calc_sum(value: &[u8;16]) -> u64 {
let mut result = 0;
for i in 0..8 {
let index = i*2;
result += u64::from( BigEndian::read_u16(&value[index..(index + 2)]) );
}
result
}
self.calc_checksum_post_ip(calc_sum(source) +
calc_sum(destination) +
IpTrafficClass::Udp as u64 +
u64::from( self.length ),
payload)
}
fn calc_checksum_post_ip(&self, ip_pseudo_header_sum: u64, payload: &[u8]) -> u16 {
let mut sum = ip_pseudo_header_sum +
u64::from( self.source_port ) + u64::from( self.destination_port ) +
u64::from( self.length );
for i in 0..(payload.len()/2) {
sum += u64::from( BigEndian::read_u16(&payload[i*2..i*2 + 2]) );
}
if payload.len() % 2 == 1 {
sum += u64::from( BigEndian::read_u16(&[*payload.last().unwrap(), 0]));
}
let carry_add = (sum & 0xffff) +
((sum >> 16) & 0xffff) +
((sum >> 32) & 0xffff) +
((sum >> 48) & 0xffff);
let result = ((carry_add & 0xffff) + (carry_add >> 16)) as u16;
if 0xffff == result {
result } else {
!result
}
}
pub fn read_from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), ReadError> {
Ok((
UdpHeaderSlice::from_slice(slice)?.to_header(),
&slice[UdpHeader::SERIALIZED_SIZE..]
))
}
pub fn read<T: io::Read + io::Seek + Sized>(reader: &mut T) -> Result<UdpHeader, io::Error> {
Ok(UdpHeader{
source_port: reader.read_u16::<BigEndian>()?,
destination_port: reader.read_u16::<BigEndian>()?,
length: reader.read_u16::<BigEndian>()?,
checksum: reader.read_u16::<BigEndian>()?
})
}
pub fn write<T: io::Write + Sized>(&self, writer: &mut T) -> Result<(), WriteError> {
writer.write_u16::<BigEndian>(self.source_port)?;
writer.write_u16::<BigEndian>(self.destination_port)?;
writer.write_u16::<BigEndian>(self.length)?;
writer.write_u16::<BigEndian>(self.checksum)?;
Ok(())
}
}
impl SerializedSize for UdpHeader {
const SERIALIZED_SIZE: usize = 8;
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UdpHeaderSlice<'a> {
slice: &'a [u8]
}
impl<'a> UdpHeaderSlice<'a> {
pub fn from_slice(slice: &'a[u8]) -> Result<UdpHeaderSlice<'a>, ReadError> {
use crate::ReadError::*;
if slice.len() < UdpHeader::SERIALIZED_SIZE {
return Err(UnexpectedEndOfSlice(UdpHeader::SERIALIZED_SIZE));
}
Ok(UdpHeaderSlice{
slice: &slice[..UdpHeader::SERIALIZED_SIZE]
})
}
pub fn slice(&self) -> &'a [u8] {
self.slice
}
pub fn source_port(&self) -> u16 {
BigEndian::read_u16(&self.slice[..2])
}
pub fn destination_port(&self) -> u16 {
BigEndian::read_u16(&self.slice[2..4])
}
pub fn length(&self) -> u16 {
BigEndian::read_u16(&self.slice[4..6])
}
pub fn checksum(&self) -> u16 {
BigEndian::read_u16(&self.slice[6..8])
}
pub fn to_header(&self) -> UdpHeader {
UdpHeader {
source_port: self.source_port(),
destination_port: self.destination_port(),
length: self.length(),
checksum: self.checksum()
}
}
}