1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
//                    Version 2, December 2004
//
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
//
// Everyone is permitted to copy and distribute verbatim or modified
// copies of this license document, and changing it is allowed as long
// as the name is changed.
//
//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
//   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
//
//  0. You just DO WHAT THE FUCK YOU WANT TO.

mod packet;
pub use self::packet::Packet;

mod builder;
pub use self::builder::Builder;

use crate::ip;
use crate::ip::Protocol;

/// Calculate the checksum for a UDP packet.
///
/// # Note
///
/// Since the checksum for UDP packets includes a pseudo-header based on the
/// enclosing IP packet, one has to be given.
pub fn checksum<B: AsRef<[u8]>>(ip: &ip::Packet<B>, buffer: &[u8]) -> u16 {
	use std::io::Cursor;
	use byteorder::{WriteBytesExt, ReadBytesExt, BigEndian};

	let mut prefix = [0u8; 40];
	match *ip {
		ip::Packet::V4(ref packet) => {
			prefix[0 .. 4].copy_from_slice(&packet.source().octets());
			prefix[4 .. 8].copy_from_slice(&packet.destination().octets());

			prefix[9] = Protocol::Udp.into();
			Cursor::new(&mut prefix[10 ..])
				.write_u16::<BigEndian>(buffer.len() as u16).unwrap();
		}

		ip::Packet::V6(ref _packet) => {
			unimplemented!();
		}
	};

	let mut result = 0xffffu32;
	let mut buffer = Cursor::new(buffer);
	let mut prefix = match *ip {
		ip::Packet::V4(_) =>
			Cursor::new(&prefix[0 .. 12]),

		ip::Packet::V6(_) =>
			Cursor::new(&prefix[0 .. 40]),
	};

	while let Ok(value) = prefix.read_u16::<BigEndian>() {
		result += u32::from(value);

		if result > 0xffff {
			result -= 0xffff;
		}
	}

	while let Ok(value) = buffer.read_u16::<BigEndian>() {
		// Skip checksum field.
		if buffer.position() == 8 {
			continue;
		}

		result += u32::from(value);

		if result > 0xffff {
			result -= 0xffff;
		}
	}

	!result as u16
}