1use std::default::Default;
5use nom::{
6 number::complete::{le_u16, be_u16, le_u8, le_u32, le_u64},
7 combinator::rest,
8 bytes::complete::take,
9};
10
11use tox_binary_io::*;
12use tox_crypto::*;
13use tox_packet::dht::packed_node::*;
14use tox_packet::toxid::{NoSpam, NOSPAMBYTES};
15use tox_packet::packed_node::*;
16
17const REQUEST_MSG_LEN: usize = 1024;
18
19const SECTION_MAGIC: &[u8; 2] = &[0xce, 0x01];
21
22#[derive(Clone, Debug, Eq, PartialEq)]
27pub struct NospamKeys {
28 pub nospam: NoSpam,
30 pub pk: PublicKey,
32 pub sk: SecretKey,
34}
35
36pub const NOSPAMKEYSBYTES: usize = NOSPAMBYTES + PUBLICKEYBYTES + SECRETKEYBYTES;
38
39impl NospamKeys {
40 pub fn random() -> Self {
42 let nospam = NoSpam::random();
43 let (pk, sk) = gen_keypair();
44 NospamKeys {
45 nospam,
46 pk,
47 sk
48 }
49 }
50}
51
52impl FromBytes for NospamKeys {
57 named!(from_bytes<NospamKeys>, do_parse!(
58 tag!([0x01,0x00]) >>
59 tag!(SECTION_MAGIC) >>
60 nospam: call!(NoSpam::from_bytes) >>
61 pk: call!(PublicKey::from_bytes) >>
62 sk: call!(SecretKey::from_bytes) >>
63 (NospamKeys {
64 nospam,
65 pk,
66 sk
67 })
68 ));
69}
70
71impl ToBytes for NospamKeys {
72 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
73 do_gen!(buf,
74 gen_le_u16!(0x0001) >>
75 gen_slice!(SECTION_MAGIC) >>
76 gen_slice!(self.nospam.0) >>
77 gen_slice!(self.pk.as_ref()) >>
78 gen_slice!(self.sk.0)
79 )
80 }
81}
82
83#[derive(Clone, Debug, Default, Eq, PartialEq)]
86pub struct Name(pub Vec<u8>);
87
88pub const NAME_LEN: usize = 128;
90
91impl FromBytes for Name {
95 named!(from_bytes<Name>, do_parse!(
96 tag!([0x04,0x00]) >>
97 tag!(SECTION_MAGIC) >>
98 name_bytes: rest >>
99 name: value!(name_bytes.to_vec()) >>
100 (Name(name))
101 ));
102}
103
104impl ToBytes for Name {
105 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
106 do_gen!(buf,
107 gen_le_u16!(0x0004) >>
108 gen_slice!(SECTION_MAGIC) >>
109 gen_slice!(self.0.as_slice())
110 )
111 }
112}
113
114#[derive(Clone, Debug, Default, Eq, PartialEq)]
119pub struct DhtState(pub Vec<PackedNode>);
120
121const DHT_MAGICAL: u32 = 0x0159_000d;
123
124const DHT_SECTION_TYPE: u16 = 0x0004;
129
130const DHT_2ND_MAGICAL: u16 = 0x11ce;
135
136impl FromBytes for DhtState {
137 named!(from_bytes<DhtState>, do_parse!(
138 tag!([0x02,0x00]) >>
139 tag!(SECTION_MAGIC) >>
140 verify!(le_u32, |value| *value == DHT_MAGICAL) >> num_of_bytes: le_u32 >>
142 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)) >>
145 (DhtState(nodes))
146 ));
147}
148
149impl ToBytes for DhtState {
150 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
151 let start_idx = buf.1;
152
153 let (buf, idx) = do_gen!(buf,
154 gen_le_u16!(0x0002) >>
155 gen_slice!(SECTION_MAGIC) >>
156 gen_le_u32!(DHT_MAGICAL as u32) >>
157 gen_skip!(4) >>
158 gen_le_u16!(DHT_SECTION_TYPE as u16) >>
159 gen_le_u16!(DHT_2ND_MAGICAL as u16) >>
160 gen_many_ref!(&self.0, |buf, node| PackedNode::to_bytes(node, buf))
161 )?;
162
163 let len = (idx - start_idx - 16) as u32;
164 buf[start_idx + 8..start_idx + 12].copy_from_slice(&u32::to_le_bytes(len));
165 Ok((buf, idx))
166 }
167}
168
169#[derive(Copy, Clone, Debug, Eq, PartialEq)]
175pub enum FriendStatus {
176 NotFriend = 0,
178 Added = 1,
180 FrSent = 2,
182 Confirmed = 3,
185 Online = 4,
187}
188
189impl FromBytes for FriendStatus {
190 named!(from_bytes<FriendStatus>, switch!(le_u8,
191 0 => value!(FriendStatus::NotFriend) |
192 1 => value!(FriendStatus::Added) |
193 2 => value!(FriendStatus::FrSent) |
194 3 => value!(FriendStatus::Confirmed) |
195 4 => value!(FriendStatus::Online)
196 ));
197}
198
199#[derive(Clone, Copy, Debug, Eq, PartialEq)]
205pub enum UserWorkingStatus {
206 Online = 0,
208 Away = 1,
210 Busy = 2,
212}
213
214impl Default for UserWorkingStatus {
216 fn default() -> Self {
217 UserWorkingStatus::Online
218 }
219}
220
221impl FromBytes for UserWorkingStatus {
222 named!(from_bytes<UserWorkingStatus>,
223 switch!(le_u8,
224 0 => value!(UserWorkingStatus::Online) |
225 1 => value!(UserWorkingStatus::Away) |
226 2 => value!(UserWorkingStatus::Busy)
227 )
228 );
229}
230
231pub const USER_STATUS_LEN: usize = 1;
233
234#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
236pub struct UserStatus(UserWorkingStatus);
237
238impl FromBytes for UserStatus {
239 named!(from_bytes<UserStatus>, do_parse!(
240 tag!([0x06,0x00]) >>
241 tag!(SECTION_MAGIC) >>
242 user_status : call!(UserWorkingStatus::from_bytes) >>
243 (UserStatus(user_status))
244 ));
245}
246
247impl ToBytes for UserStatus {
248 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
249 do_gen!(buf,
250 gen_le_u16!(0x0006) >>
251 gen_slice!(SECTION_MAGIC) >>
252 gen_le_u8!(self.0 as u8)
253 )
254 }
255}
256
257#[derive(Clone, Debug, Default, Eq, PartialEq)]
262pub struct StatusMsg(pub Vec<u8>);
263
264pub const STATUS_MSG_LEN: usize = 1007;
267
268impl ToBytes for StatusMsg {
269 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
270 do_gen!(buf,
271 gen_le_u16!(0x0005) >>
272 gen_slice!(SECTION_MAGIC) >>
273 gen_slice!(self.0.as_slice())
274 )
275 }
276}
277
278impl FromBytes for StatusMsg {
279 named!(from_bytes<StatusMsg>, do_parse!(
280 tag!([0x05,0x00]) >>
281 tag!(SECTION_MAGIC) >>
282 status_msg_bytes: rest >>
283 status_msg: value!(status_msg_bytes.to_vec()) >>
284 (StatusMsg(status_msg))
285 ));
286}
287
288#[derive(Clone, Debug, Default, Eq, PartialEq)]
290pub struct TcpRelays(pub Vec<TcpUdpPackedNode>);
291
292impl FromBytes for TcpRelays {
293 named!(from_bytes<TcpRelays>, do_parse!(
294 tag!([0x0a, 0x00]) >>
295 tag!(SECTION_MAGIC) >>
296 nodes: many0!(TcpUdpPackedNode::from_bytes) >>
297 (TcpRelays(nodes))
298 ));
299}
300
301impl ToBytes for TcpRelays {
302 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
303 do_gen!(buf,
304 gen_le_u16!(0x000a) >>
305 gen_slice!(SECTION_MAGIC) >>
306 gen_many_ref!(&self.0, |buf, node| TcpUdpPackedNode::to_bytes(node, buf))
307 )
308 }
309}
310
311#[derive(Clone, Debug, Default, Eq, PartialEq)]
313pub struct PathNodes(pub Vec<TcpUdpPackedNode>);
314
315impl FromBytes for PathNodes {
316 named!(from_bytes<PathNodes>, do_parse!(
317 tag!([0x0b, 0x00]) >>
318 tag!(SECTION_MAGIC) >>
319 nodes: many0!(TcpUdpPackedNode::from_bytes) >>
320 (PathNodes(nodes))
321 ));
322}
323
324impl ToBytes for PathNodes {
325 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
326 do_gen!(buf,
327 gen_le_u16!(0x000b) >>
328 gen_slice!(SECTION_MAGIC) >>
329 gen_many_ref!(&self.0, |buf, node| TcpUdpPackedNode::to_bytes(node, buf))
330 )
331 }
332}
333
334#[derive(Clone, Debug, Eq, PartialEq)]
347pub struct FriendState {
348 friend_status: FriendStatus,
349 pk: PublicKey,
350 fr_msg: Vec<u8>,
352 name: Name,
354 status_msg: StatusMsg,
355 user_status: UserWorkingStatus,
356 nospam: NoSpam,
357 last_seen: u64,
359}
360
361pub const FRIENDSTATEBYTES: usize = 1 + PUBLICKEYBYTES
364 + REQUEST_MSG_LEN
365 + 1
366 + 2
367 + NAME_LEN
368 + 2
369 + STATUS_MSG_LEN
370 + 1
371 + 2
372 + 1
373 + 3
374 + NOSPAMBYTES
375 + 8;
376
377impl FromBytes for FriendState {
378 named!(from_bytes<FriendState>, do_parse!(
379 friend_status: call!(FriendStatus::from_bytes) >>
380 pk: call!(PublicKey::from_bytes) >>
381 fr_msg_bytes: take!(REQUEST_MSG_LEN) >>
382 _padding1: take!(1) >>
383 fr_msg_len: be_u16 >>
384 verify!(value!(fr_msg_len), |len| *len <= REQUEST_MSG_LEN as u16) >>
385 fr_msg: value!(fr_msg_bytes[..fr_msg_len as usize].to_vec()) >>
386 name_bytes: take!(NAME_LEN) >>
387 name_len: be_u16 >>
388 verify!(value!(name_len), |len| *len <= NAME_LEN as u16) >>
389 name: value!(Name(name_bytes[..name_len as usize].to_vec())) >>
390 status_msg_bytes: take!(STATUS_MSG_LEN) >>
391 _padding2: take!(1) >>
392 status_msg_len: be_u16 >>
393 verify!(value!(status_msg_len), |len| *len <= STATUS_MSG_LEN as u16) >>
394 status_msg: value!(StatusMsg(status_msg_bytes[..status_msg_len as usize].to_vec())) >>
395 user_status: call!(UserWorkingStatus::from_bytes) >>
396 _padding3: take!(3) >>
397 nospam: call!(NoSpam::from_bytes) >>
398 last_seen: le_u64 >>
399 (FriendState {
400 friend_status,
401 pk,
402 fr_msg,
403 name,
404 status_msg,
405 user_status,
406 nospam,
407 last_seen,
408 })
409 ));
410}
411
412impl ToBytes for FriendState {
413 #[allow(clippy::cognitive_complexity)]
414 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
415 let mut fr_msg_pad = self.fr_msg.clone();
416 let mut name_pad = self.name.0.clone();
417 let mut status_msg_pad = self.status_msg.0.clone();
418 fr_msg_pad.resize(REQUEST_MSG_LEN, 0);
419 name_pad.resize(NAME_LEN, 0);
420 status_msg_pad.resize(STATUS_MSG_LEN, 0);
421
422 do_gen!(buf,
423 gen_le_u8!(self.friend_status as u8) >>
424 gen_slice!(self.pk.as_ref()) >>
425 gen_slice!(fr_msg_pad.as_slice()) >>
426 gen_le_u8!(0) >>
427 gen_be_u16!(self.fr_msg.len() as u16) >>
428 gen_slice!(name_pad.as_slice()) >>
429 gen_be_u16!(self.name.0.len() as u16) >>
430 gen_slice!(status_msg_pad.as_slice()) >>
431 gen_le_u8!(0) >>
432 gen_be_u16!(self.status_msg.0.len() as u16) >>
433 gen_le_u8!(self.user_status as u8) >>
434 gen_le_u8!(0) >>
435 gen_le_u16!(0) >>
436 gen_slice!(self.nospam.0) >>
437 gen_le_u64!(self.last_seen)
438 )
439 }
440}
441
442#[derive(Clone, Debug, Default, Eq, PartialEq)]
445pub struct Friends(pub Vec<FriendState>);
446
447impl FromBytes for Friends {
448 named!(from_bytes<Friends>, do_parse!(
449 tag!([0x03, 0x00]) >>
450 tag!(SECTION_MAGIC) >>
451 friends: many0!(flat_map!(take(FRIENDSTATEBYTES), FriendState::from_bytes)) >>
452 (Friends(friends))
453 ));
454}
455
456impl ToBytes for Friends {
457 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
458 do_gen!(buf,
459 gen_le_u16!(0x0003) >>
460 gen_slice!(SECTION_MAGIC) >>
461 gen_many_ref!(&self.0, |buf, friend| FriendState::to_bytes(friend, buf))
462 )
463 }
464}
465
466#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
468pub struct Eof;
469
470impl FromBytes for Eof {
471 named!(from_bytes<Eof>, do_parse!(
472 tag!([0xff, 0x00]) >>
473 tag!(SECTION_MAGIC) >>
474 (Eof)
475 ));
476}
477
478impl ToBytes for Eof {
479 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
480 do_gen!(buf,
481 gen_le_u16!(0x00ff) >>
482 gen_slice!(SECTION_MAGIC)
483 )
484 }
485}
486
487#[derive(Clone, Debug, Eq, PartialEq)]
492pub enum Section {
493 NospamKeys(NospamKeys),
499 DhtState(DhtState),
504 Friends(Friends),
510 Name(Name),
515 StatusMsg(StatusMsg),
520 UserStatus(UserStatus),
525 TcpRelays(TcpRelays),
530 PathNodes(PathNodes),
536 Eof(Eof),
538}
539
540impl FromBytes for Section {
541 named!(from_bytes<Section>, alt!(
542 map!(NospamKeys::from_bytes, Section::NospamKeys) |
543 map!(DhtState::from_bytes, Section::DhtState) |
544 map!(Friends::from_bytes, Section::Friends) |
545 map!(Name::from_bytes, Section::Name) |
546 map!(StatusMsg::from_bytes, Section::StatusMsg) |
547 map!(UserStatus::from_bytes, Section::UserStatus) |
548 map!(TcpRelays::from_bytes, Section::TcpRelays) |
549 map!(PathNodes::from_bytes, Section::PathNodes) |
550 map!(Eof::from_bytes, Section::Eof)
551 ));
552}
553
554impl ToBytes for Section {
555 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
556 let (buf, start_idx) = buf;
557
558 if buf.len() < start_idx + 4 {
559 return Err(GenError::BufferTooSmall(start_idx + 4));
560 }
561
562 let buf = (buf, start_idx + 4);
563 let (buf, idx) = match *self {
564 Section::NospamKeys(ref p) => p.to_bytes(buf),
565 Section::DhtState(ref p) => p.to_bytes(buf),
566 Section::Friends(ref p) => p.to_bytes(buf),
567 Section::Name(ref p) => p.to_bytes(buf),
568 Section::StatusMsg(ref p) => p.to_bytes(buf),
569 Section::UserStatus(ref p) => p.to_bytes(buf),
570 Section::TcpRelays(ref p) => p.to_bytes(buf),
571 Section::PathNodes(ref p) => p.to_bytes(buf),
572 Section::Eof(ref p) => p.to_bytes(buf),
573 }?;
574
575 let len = (idx - start_idx - 8) as u32;
576 buf[start_idx..start_idx + 4].copy_from_slice(&u32::to_le_bytes(len));
577 Ok((buf, idx))
578 }
579}
580
581const STATE_MAGIC: &[u8; 4] = &[0x1f, 0x1b, 0xed, 0x15];
583
584#[derive(Clone, Debug, Eq, PartialEq)]
589pub struct State {
590 sections: Vec<Section>,
591}
592
593impl FromBytes for State {
594 named!(from_bytes<State>, do_parse!(
595 tag!(&[0; 4][..]) >>
596 tag!(STATE_MAGIC) >>
597 sections: many0!(flat_map!(length_data!(map!(le_u32, |len| len + 4)), Section::from_bytes)) >>
598 (State {
599 sections: sections.to_vec(),
600 })
601 ));
602}
603
604impl ToBytes for State {
605 fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
606 do_gen!(buf,
607 gen_slice!([0; 4]) >>
608 gen_slice!(STATE_MAGIC) >>
609 gen_many_ref!(&self.sections, |buf, section| Section::to_bytes(section, buf))
610 )
611 }
612}
613
614#[cfg(test)]
615mod tests {
616 use super::*;
617
618 use tox_packet::ip_port::*;
619
620 encode_decode_test!(
621 tox_crypto::crypto_init().unwrap(),
622 no_spam_keys_encode_decode,
623 NospamKeys::random()
624 );
625
626 encode_decode_test!(
627 tox_crypto::crypto_init().unwrap(),
628 dht_state_encode_decode,
629 DhtState(vec![
630 PackedNode {
631 pk: gen_keypair().0,
632 saddr: "1.2.3.4:1234".parse().unwrap(),
633 },
634 PackedNode {
635 pk: gen_keypair().0,
636 saddr: "1.2.3.5:1235".parse().unwrap(),
637 },
638 ])
639 );
640
641 encode_decode_test!(
642 tox_crypto::crypto_init().unwrap(),
643 friends_encode_decode,
644 Friends(vec![
645 FriendState {
646 friend_status: FriendStatus::Added,
647 pk: gen_keypair().0,
648 fr_msg: b"test msg".to_vec(),
649 name: Name(b"test name".to_vec()),
650 status_msg: StatusMsg(b"test status msg".to_vec()),
651 user_status: UserWorkingStatus::Online,
652 nospam: NoSpam([7; NOSPAMBYTES]),
653 last_seen: 1234,
654 },
655 FriendState {
656 friend_status: FriendStatus::Added,
657 pk: gen_keypair().0,
658 fr_msg: b"test msg2".to_vec(),
659 name: Name(b"test name2".to_vec()),
660 status_msg: StatusMsg(b"test status msg2".to_vec()),
661 user_status: UserWorkingStatus::Online,
662 nospam: NoSpam([8; NOSPAMBYTES]),
663 last_seen: 1235,
664 },
665 ])
666 );
667
668 encode_decode_test!(
669 tox_crypto::crypto_init().unwrap(),
670 friend_state_encode_decode,
671 FriendState {
672 friend_status: FriendStatus::Added,
673 pk: gen_keypair().0,
674 fr_msg: b"test msg".to_vec(),
675 name: Name(b"test name".to_vec()),
676 status_msg: StatusMsg(b"test status msg".to_vec()),
677 user_status: UserWorkingStatus::Online,
678 nospam: NoSpam([7; NOSPAMBYTES]),
679 last_seen: 1234,
680 }
681 );
682
683 encode_decode_test!(
684 tox_crypto::crypto_init().unwrap(),
685 name_encode_decode,
686 Name(vec![0,1,2,3,4])
687 );
688
689 encode_decode_test!(
690 tox_crypto::crypto_init().unwrap(),
691 status_msg_encode_decode,
692 StatusMsg(vec![0,1,2,3,4,5])
693 );
694
695 encode_decode_test!(
696 tox_crypto::crypto_init().unwrap(),
697 eof_encode_decode,
698 Eof
699 );
700
701 encode_decode_test!(
702 tox_crypto::crypto_init().unwrap(),
703 user_status_encode_decode,
704 UserStatus(UserWorkingStatus::Online)
705 );
706
707 encode_decode_test!(
708 tox_crypto::crypto_init().unwrap(),
709 tcp_relays_encode_decode,
710 TcpRelays(vec![
711 TcpUdpPackedNode {
712 pk: gen_keypair().0,
713 ip_port: IpPort {
714 protocol: ProtocolType::TCP,
715 ip_addr: "1.2.3.4".parse().unwrap(),
716 port: 1234,
717 },
718 },
719 TcpUdpPackedNode {
720 pk: gen_keypair().0,
721 ip_port: IpPort {
722 protocol: ProtocolType::UDP,
723 ip_addr: "1.2.3.5".parse().unwrap(),
724 port: 12345,
725 },
726 },
727 ])
728 );
729
730 encode_decode_test!(
731 tox_crypto::crypto_init().unwrap(),
732 path_nodes_encode_decode,
733 PathNodes(vec![
734 TcpUdpPackedNode {
735 pk: gen_keypair().0,
736 ip_port: IpPort {
737 protocol: ProtocolType::TCP,
738 ip_addr: "1.2.3.4".parse().unwrap(),
739 port: 1234,
740 },
741 },
742 TcpUdpPackedNode {
743 pk: gen_keypair().0,
744 ip_port: IpPort {
745 protocol: ProtocolType::UDP,
746 ip_addr: "1.2.3.5".parse().unwrap(),
747 port: 12345,
748 },
749 },
750 ])
751 );
752
753 encode_decode_test!(
754 tox_crypto::crypto_init().unwrap(),
755 state_encode_decode,
756 State {
757 sections: vec![
758 Section::NospamKeys(NospamKeys::random()),
759 Section::DhtState(DhtState(vec![
760 PackedNode {
761 pk: gen_keypair().0,
762 saddr: "1.2.3.4:1234".parse().unwrap(),
763 },
764 PackedNode {
765 pk: gen_keypair().0,
766 saddr: "1.2.3.5:1235".parse().unwrap(),
767 },
768 ])),
769 Section::Friends(Friends(vec![
770 FriendState {
771 friend_status: FriendStatus::Added,
772 pk: gen_keypair().0,
773 fr_msg: b"test msg".to_vec(),
774 name: Name(b"test name".to_vec()),
775 status_msg: StatusMsg(b"test status msg".to_vec()),
776 user_status: UserWorkingStatus::Online,
777 nospam: NoSpam([7; NOSPAMBYTES]),
778 last_seen: 1234,
779 },
780 FriendState {
781 friend_status: FriendStatus::Added,
782 pk: gen_keypair().0,
783 fr_msg: b"test msg2".to_vec(),
784 name: Name(b"test name2".to_vec()),
785 status_msg: StatusMsg(b"test status msg2".to_vec()),
786 user_status: UserWorkingStatus::Online,
787 nospam: NoSpam([8; NOSPAMBYTES]),
788 last_seen: 1235,
789 },
790 ])),
791 Section::Name(Name(vec![0,1,2,3,4])),
792 Section::StatusMsg(StatusMsg(vec![0,1,2,3,4,5])),
793 Section::UserStatus(UserStatus(UserWorkingStatus::Online)),
794 Section::TcpRelays(TcpRelays(vec![
795 TcpUdpPackedNode {
796 pk: gen_keypair().0,
797 ip_port: IpPort {
798 protocol: ProtocolType::TCP,
799 ip_addr: "1.2.3.4".parse().unwrap(),
800 port: 1234,
801 },
802 },
803 TcpUdpPackedNode {
804 pk: gen_keypair().0,
805 ip_port: IpPort {
806 protocol: ProtocolType::UDP,
807 ip_addr: "1.2.3.5".parse().unwrap(),
808 port: 12345,
809 },
810 },
811 ])),
812 Section::PathNodes(PathNodes(vec![
813 TcpUdpPackedNode {
814 pk: gen_keypair().0,
815 ip_port: IpPort {
816 protocol: ProtocolType::TCP,
817 ip_addr: "1.2.3.4".parse().unwrap(),
818 port: 1234,
819 },
820 },
821 TcpUdpPackedNode {
822 pk: gen_keypair().0,
823 ip_port: IpPort {
824 protocol: ProtocolType::UDP,
825 ip_addr: "1.2.3.5".parse().unwrap(),
826 port: 12345,
827 },
828 },
829 ])),
830 Section::Eof(Eof),
831 ],
832 }
833 );
834}