use std::default::Default;
use nom::{
number::complete::{le_u16, be_u16, le_u8, le_u32, le_u64},
combinator::rest,
bytes::complete::take,
};
use tox_binary_io::*;
use tox_crypto::*;
use tox_packet::dht::packed_node::*;
use tox_packet::toxid::{NoSpam, NOSPAMBYTES};
use tox_packet::packed_node::*;
const REQUEST_MSG_LEN: usize = 1024;
const SECTION_MAGIC: &[u8; 2] = &[0xce, 0x01];
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NospamKeys {
pub nospam: NoSpam,
pub pk: PublicKey,
pub sk: SecretKey,
}
pub const NOSPAMKEYSBYTES: usize = NOSPAMBYTES + PUBLICKEYBYTES + SECRETKEYBYTES;
impl NospamKeys {
pub fn random() -> Self {
let nospam = NoSpam::random();
let (pk, sk) = gen_keypair();
NospamKeys {
nospam,
pk,
sk
}
}
}
impl FromBytes for NospamKeys {
named!(from_bytes<NospamKeys>, do_parse!(
tag!([0x01,0x00]) >>
tag!(SECTION_MAGIC) >>
nospam: call!(NoSpam::from_bytes) >>
pk: call!(PublicKey::from_bytes) >>
sk: call!(SecretKey::from_bytes) >>
(NospamKeys {
nospam,
pk,
sk
})
));
}
impl ToBytes for NospamKeys {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_le_u16!(0x0001) >>
gen_slice!(SECTION_MAGIC) >>
gen_slice!(self.nospam.0) >>
gen_slice!(self.pk.as_ref()) >>
gen_slice!(self.sk.0)
)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Name(pub Vec<u8>);
pub const NAME_LEN: usize = 128;
impl FromBytes for Name {
named!(from_bytes<Name>, do_parse!(
tag!([0x04,0x00]) >>
tag!(SECTION_MAGIC) >>
name_bytes: rest >>
name: value!(name_bytes.to_vec()) >>
(Name(name))
));
}
impl ToBytes for Name {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_le_u16!(0x0004) >>
gen_slice!(SECTION_MAGIC) >>
gen_slice!(self.0.as_slice())
)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DhtState(pub Vec<PackedNode>);
const DHT_MAGICAL: u32 = 0x0159_000d;
const DHT_SECTION_TYPE: u16 = 0x0004;
const DHT_2ND_MAGICAL: u16 = 0x11ce;
impl FromBytes for DhtState {
named!(from_bytes<DhtState>, do_parse!(
tag!([0x02,0x00]) >>
tag!(SECTION_MAGIC) >>
verify!(le_u32, |value| *value == DHT_MAGICAL) >> num_of_bytes: le_u32 >>
verify!(le_u16, |value| *value == DHT_SECTION_TYPE) >> verify!(le_u16, |value| *value == DHT_2ND_MAGICAL) >> nodes: flat_map!(take(num_of_bytes), many0!(PackedNode::from_bytes)) >>
(DhtState(nodes))
));
}
impl ToBytes for DhtState {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
let start_idx = buf.1;
let (buf, idx) = do_gen!(buf,
gen_le_u16!(0x0002) >>
gen_slice!(SECTION_MAGIC) >>
gen_le_u32!(DHT_MAGICAL as u32) >>
gen_skip!(4) >>
gen_le_u16!(DHT_SECTION_TYPE as u16) >>
gen_le_u16!(DHT_2ND_MAGICAL as u16) >>
gen_many_ref!(&self.0, |buf, node| PackedNode::to_bytes(node, buf))
)?;
let len = (idx - start_idx - 16) as u32;
buf[start_idx + 8..start_idx + 12].copy_from_slice(&u32::to_le_bytes(len));
Ok((buf, idx))
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FriendStatus {
NotFriend = 0,
Added = 1,
FrSent = 2,
Confirmed = 3,
Online = 4,
}
impl FromBytes for FriendStatus {
named!(from_bytes<FriendStatus>, switch!(le_u8,
0 => value!(FriendStatus::NotFriend) |
1 => value!(FriendStatus::Added) |
2 => value!(FriendStatus::FrSent) |
3 => value!(FriendStatus::Confirmed) |
4 => value!(FriendStatus::Online)
));
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UserWorkingStatus {
Online = 0,
Away = 1,
Busy = 2,
}
impl Default for UserWorkingStatus {
fn default() -> Self {
UserWorkingStatus::Online
}
}
impl FromBytes for UserWorkingStatus {
named!(from_bytes<UserWorkingStatus>,
switch!(le_u8,
0 => value!(UserWorkingStatus::Online) |
1 => value!(UserWorkingStatus::Away) |
2 => value!(UserWorkingStatus::Busy)
)
);
}
pub const USER_STATUS_LEN: usize = 1;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct UserStatus(UserWorkingStatus);
impl FromBytes for UserStatus {
named!(from_bytes<UserStatus>, do_parse!(
tag!([0x06,0x00]) >>
tag!(SECTION_MAGIC) >>
user_status : call!(UserWorkingStatus::from_bytes) >>
(UserStatus(user_status))
));
}
impl ToBytes for UserStatus {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_le_u16!(0x0006) >>
gen_slice!(SECTION_MAGIC) >>
gen_le_u8!(self.0 as u8)
)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct StatusMsg(pub Vec<u8>);
pub const STATUS_MSG_LEN: usize = 1007;
impl ToBytes for StatusMsg {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_le_u16!(0x0005) >>
gen_slice!(SECTION_MAGIC) >>
gen_slice!(self.0.as_slice())
)
}
}
impl FromBytes for StatusMsg {
named!(from_bytes<StatusMsg>, do_parse!(
tag!([0x05,0x00]) >>
tag!(SECTION_MAGIC) >>
status_msg_bytes: rest >>
status_msg: value!(status_msg_bytes.to_vec()) >>
(StatusMsg(status_msg))
));
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TcpRelays(pub Vec<TcpUdpPackedNode>);
impl FromBytes for TcpRelays {
named!(from_bytes<TcpRelays>, do_parse!(
tag!([0x0a, 0x00]) >>
tag!(SECTION_MAGIC) >>
nodes: many0!(TcpUdpPackedNode::from_bytes) >>
(TcpRelays(nodes))
));
}
impl ToBytes for TcpRelays {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_le_u16!(0x000a) >>
gen_slice!(SECTION_MAGIC) >>
gen_many_ref!(&self.0, |buf, node| TcpUdpPackedNode::to_bytes(node, buf))
)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct PathNodes(pub Vec<TcpUdpPackedNode>);
impl FromBytes for PathNodes {
named!(from_bytes<PathNodes>, do_parse!(
tag!([0x0b, 0x00]) >>
tag!(SECTION_MAGIC) >>
nodes: many0!(TcpUdpPackedNode::from_bytes) >>
(PathNodes(nodes))
));
}
impl ToBytes for PathNodes {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_le_u16!(0x000b) >>
gen_slice!(SECTION_MAGIC) >>
gen_many_ref!(&self.0, |buf, node| TcpUdpPackedNode::to_bytes(node, buf))
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FriendState {
friend_status: FriendStatus,
pk: PublicKey,
fr_msg: Vec<u8>,
name: Name,
status_msg: StatusMsg,
user_status: UserWorkingStatus,
nospam: NoSpam,
last_seen: u64,
}
pub const FRIENDSTATEBYTES: usize = 1 + PUBLICKEYBYTES
+ REQUEST_MSG_LEN
+ 1
+ 2
+ NAME_LEN
+ 2
+ STATUS_MSG_LEN
+ 1
+ 2
+ 1
+ 3
+ NOSPAMBYTES
+ 8;
impl FromBytes for FriendState {
named!(from_bytes<FriendState>, do_parse!(
friend_status: call!(FriendStatus::from_bytes) >>
pk: call!(PublicKey::from_bytes) >>
fr_msg_bytes: take!(REQUEST_MSG_LEN) >>
_padding1: take!(1) >>
fr_msg_len: be_u16 >>
verify!(value!(fr_msg_len), |len| *len <= REQUEST_MSG_LEN as u16) >>
fr_msg: value!(fr_msg_bytes[..fr_msg_len as usize].to_vec()) >>
name_bytes: take!(NAME_LEN) >>
name_len: be_u16 >>
verify!(value!(name_len), |len| *len <= NAME_LEN as u16) >>
name: value!(Name(name_bytes[..name_len as usize].to_vec())) >>
status_msg_bytes: take!(STATUS_MSG_LEN) >>
_padding2: take!(1) >>
status_msg_len: be_u16 >>
verify!(value!(status_msg_len), |len| *len <= STATUS_MSG_LEN as u16) >>
status_msg: value!(StatusMsg(status_msg_bytes[..status_msg_len as usize].to_vec())) >>
user_status: call!(UserWorkingStatus::from_bytes) >>
_padding3: take!(3) >>
nospam: call!(NoSpam::from_bytes) >>
last_seen: le_u64 >>
(FriendState {
friend_status,
pk,
fr_msg,
name,
status_msg,
user_status,
nospam,
last_seen,
})
));
}
impl ToBytes for FriendState {
#[allow(clippy::cognitive_complexity)]
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
let mut fr_msg_pad = self.fr_msg.clone();
let mut name_pad = self.name.0.clone();
let mut status_msg_pad = self.status_msg.0.clone();
fr_msg_pad.resize(REQUEST_MSG_LEN, 0);
name_pad.resize(NAME_LEN, 0);
status_msg_pad.resize(STATUS_MSG_LEN, 0);
do_gen!(buf,
gen_le_u8!(self.friend_status as u8) >>
gen_slice!(self.pk.as_ref()) >>
gen_slice!(fr_msg_pad.as_slice()) >>
gen_le_u8!(0) >>
gen_be_u16!(self.fr_msg.len() as u16) >>
gen_slice!(name_pad.as_slice()) >>
gen_be_u16!(self.name.0.len() as u16) >>
gen_slice!(status_msg_pad.as_slice()) >>
gen_le_u8!(0) >>
gen_be_u16!(self.status_msg.0.len() as u16) >>
gen_le_u8!(self.user_status as u8) >>
gen_le_u8!(0) >>
gen_le_u16!(0) >>
gen_slice!(self.nospam.0) >>
gen_le_u64!(self.last_seen)
)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Friends(pub Vec<FriendState>);
impl FromBytes for Friends {
named!(from_bytes<Friends>, do_parse!(
tag!([0x03, 0x00]) >>
tag!(SECTION_MAGIC) >>
friends: many0!(flat_map!(take(FRIENDSTATEBYTES), FriendState::from_bytes)) >>
(Friends(friends))
));
}
impl ToBytes for Friends {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_le_u16!(0x0003) >>
gen_slice!(SECTION_MAGIC) >>
gen_many_ref!(&self.0, |buf, friend| FriendState::to_bytes(friend, buf))
)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Eof;
impl FromBytes for Eof {
named!(from_bytes<Eof>, do_parse!(
tag!([0xff, 0x00]) >>
tag!(SECTION_MAGIC) >>
(Eof)
));
}
impl ToBytes for Eof {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_le_u16!(0x00ff) >>
gen_slice!(SECTION_MAGIC)
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Section {
NospamKeys(NospamKeys),
DhtState(DhtState),
Friends(Friends),
Name(Name),
StatusMsg(StatusMsg),
UserStatus(UserStatus),
TcpRelays(TcpRelays),
PathNodes(PathNodes),
Eof(Eof),
}
impl FromBytes for Section {
named!(from_bytes<Section>, alt!(
map!(NospamKeys::from_bytes, Section::NospamKeys) |
map!(DhtState::from_bytes, Section::DhtState) |
map!(Friends::from_bytes, Section::Friends) |
map!(Name::from_bytes, Section::Name) |
map!(StatusMsg::from_bytes, Section::StatusMsg) |
map!(UserStatus::from_bytes, Section::UserStatus) |
map!(TcpRelays::from_bytes, Section::TcpRelays) |
map!(PathNodes::from_bytes, Section::PathNodes) |
map!(Eof::from_bytes, Section::Eof)
));
}
impl ToBytes for Section {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
let (buf, start_idx) = buf;
if buf.len() < start_idx + 4 {
return Err(GenError::BufferTooSmall(start_idx + 4));
}
let buf = (buf, start_idx + 4);
let (buf, idx) = match *self {
Section::NospamKeys(ref p) => p.to_bytes(buf),
Section::DhtState(ref p) => p.to_bytes(buf),
Section::Friends(ref p) => p.to_bytes(buf),
Section::Name(ref p) => p.to_bytes(buf),
Section::StatusMsg(ref p) => p.to_bytes(buf),
Section::UserStatus(ref p) => p.to_bytes(buf),
Section::TcpRelays(ref p) => p.to_bytes(buf),
Section::PathNodes(ref p) => p.to_bytes(buf),
Section::Eof(ref p) => p.to_bytes(buf),
}?;
let len = (idx - start_idx - 8) as u32;
buf[start_idx..start_idx + 4].copy_from_slice(&u32::to_le_bytes(len));
Ok((buf, idx))
}
}
const STATE_MAGIC: &[u8; 4] = &[0x1f, 0x1b, 0xed, 0x15];
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct State {
sections: Vec<Section>,
}
impl FromBytes for State {
named!(from_bytes<State>, do_parse!(
tag!(&[0; 4][..]) >>
tag!(STATE_MAGIC) >>
sections: many0!(flat_map!(length_data!(map!(le_u32, |len| len + 4)), Section::from_bytes)) >>
(State {
sections: sections.to_vec(),
})
));
}
impl ToBytes for State {
fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
do_gen!(buf,
gen_slice!([0; 4]) >>
gen_slice!(STATE_MAGIC) >>
gen_many_ref!(&self.sections, |buf, section| Section::to_bytes(section, buf))
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tox_packet::ip_port::*;
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
no_spam_keys_encode_decode,
NospamKeys::random()
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
dht_state_encode_decode,
DhtState(vec![
PackedNode {
pk: gen_keypair().0,
saddr: "1.2.3.4:1234".parse().unwrap(),
},
PackedNode {
pk: gen_keypair().0,
saddr: "1.2.3.5:1235".parse().unwrap(),
},
])
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
friends_encode_decode,
Friends(vec![
FriendState {
friend_status: FriendStatus::Added,
pk: gen_keypair().0,
fr_msg: b"test msg".to_vec(),
name: Name(b"test name".to_vec()),
status_msg: StatusMsg(b"test status msg".to_vec()),
user_status: UserWorkingStatus::Online,
nospam: NoSpam([7; NOSPAMBYTES]),
last_seen: 1234,
},
FriendState {
friend_status: FriendStatus::Added,
pk: gen_keypair().0,
fr_msg: b"test msg2".to_vec(),
name: Name(b"test name2".to_vec()),
status_msg: StatusMsg(b"test status msg2".to_vec()),
user_status: UserWorkingStatus::Online,
nospam: NoSpam([8; NOSPAMBYTES]),
last_seen: 1235,
},
])
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
friend_state_encode_decode,
FriendState {
friend_status: FriendStatus::Added,
pk: gen_keypair().0,
fr_msg: b"test msg".to_vec(),
name: Name(b"test name".to_vec()),
status_msg: StatusMsg(b"test status msg".to_vec()),
user_status: UserWorkingStatus::Online,
nospam: NoSpam([7; NOSPAMBYTES]),
last_seen: 1234,
}
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
name_encode_decode,
Name(vec![0,1,2,3,4])
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
status_msg_encode_decode,
StatusMsg(vec![0,1,2,3,4,5])
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
eof_encode_decode,
Eof
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
user_status_encode_decode,
UserStatus(UserWorkingStatus::Online)
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
tcp_relays_encode_decode,
TcpRelays(vec![
TcpUdpPackedNode {
pk: gen_keypair().0,
ip_port: IpPort {
protocol: ProtocolType::TCP,
ip_addr: "1.2.3.4".parse().unwrap(),
port: 1234,
},
},
TcpUdpPackedNode {
pk: gen_keypair().0,
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "1.2.3.5".parse().unwrap(),
port: 12345,
},
},
])
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
path_nodes_encode_decode,
PathNodes(vec![
TcpUdpPackedNode {
pk: gen_keypair().0,
ip_port: IpPort {
protocol: ProtocolType::TCP,
ip_addr: "1.2.3.4".parse().unwrap(),
port: 1234,
},
},
TcpUdpPackedNode {
pk: gen_keypair().0,
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "1.2.3.5".parse().unwrap(),
port: 12345,
},
},
])
);
encode_decode_test!(
tox_crypto::crypto_init().unwrap(),
state_encode_decode,
State {
sections: vec![
Section::NospamKeys(NospamKeys::random()),
Section::DhtState(DhtState(vec![
PackedNode {
pk: gen_keypair().0,
saddr: "1.2.3.4:1234".parse().unwrap(),
},
PackedNode {
pk: gen_keypair().0,
saddr: "1.2.3.5:1235".parse().unwrap(),
},
])),
Section::Friends(Friends(vec![
FriendState {
friend_status: FriendStatus::Added,
pk: gen_keypair().0,
fr_msg: b"test msg".to_vec(),
name: Name(b"test name".to_vec()),
status_msg: StatusMsg(b"test status msg".to_vec()),
user_status: UserWorkingStatus::Online,
nospam: NoSpam([7; NOSPAMBYTES]),
last_seen: 1234,
},
FriendState {
friend_status: FriendStatus::Added,
pk: gen_keypair().0,
fr_msg: b"test msg2".to_vec(),
name: Name(b"test name2".to_vec()),
status_msg: StatusMsg(b"test status msg2".to_vec()),
user_status: UserWorkingStatus::Online,
nospam: NoSpam([8; NOSPAMBYTES]),
last_seen: 1235,
},
])),
Section::Name(Name(vec![0,1,2,3,4])),
Section::StatusMsg(StatusMsg(vec![0,1,2,3,4,5])),
Section::UserStatus(UserStatus(UserWorkingStatus::Online)),
Section::TcpRelays(TcpRelays(vec![
TcpUdpPackedNode {
pk: gen_keypair().0,
ip_port: IpPort {
protocol: ProtocolType::TCP,
ip_addr: "1.2.3.4".parse().unwrap(),
port: 1234,
},
},
TcpUdpPackedNode {
pk: gen_keypair().0,
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "1.2.3.5".parse().unwrap(),
port: 12345,
},
},
])),
Section::PathNodes(PathNodes(vec![
TcpUdpPackedNode {
pk: gen_keypair().0,
ip_port: IpPort {
protocol: ProtocolType::TCP,
ip_addr: "1.2.3.4".parse().unwrap(),
port: 1234,
},
},
TcpUdpPackedNode {
pk: gen_keypair().0,
ip_port: IpPort {
protocol: ProtocolType::UDP,
ip_addr: "1.2.3.5".parse().unwrap(),
port: 12345,
},
},
])),
Section::Eof(Eof),
],
}
);
}