1use crate::client::datatype::{
67 split, Address, Certificate, CloseReason, LinkProtocol, RelayCommand, Size, ZERO,
68};
69use crate::Error;
70use chrono::{DateTime, TimeZone, Utc};
71
72pub const FIXED_PAYLOAD_LEN: usize = 509;
77
78pub const AUTH_CHALLENGE_SIZE: usize = 32;
80
81pub const HASH_LEN: usize = 20;
85
86pub const CELL_TYPE_SIZE: Size = Size::Char;
88
89pub const PAYLOAD_LEN_SIZE: Size = Size::Short;
91
92pub const RELAY_DIGEST_SIZE: Size = Size::Long;
94
95pub const STREAM_ID_REQUIRED: &[RelayCommand] = &[
100 RelayCommand::Begin,
101 RelayCommand::Data,
102 RelayCommand::End,
103 RelayCommand::Connected,
104 RelayCommand::Resolve,
105 RelayCommand::Resolved,
106 RelayCommand::BeginDir,
107];
108
109pub const STREAM_ID_DISALLOWED: &[RelayCommand] = &[
114 RelayCommand::Extend,
115 RelayCommand::Extended,
116 RelayCommand::Truncate,
117 RelayCommand::Truncated,
118 RelayCommand::Drop,
119 RelayCommand::Extend2,
120 RelayCommand::Extended2,
121];
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum CellType {
157 Padding,
159 Create,
161 Created,
163 Relay,
165 Destroy,
167 CreateFast,
169 CreatedFast,
171 Versions,
173 Netinfo,
175 RelayEarly,
177 Create2,
179 Created2,
181 PaddingNegotiate,
183 VPadding,
185 Certs,
187 AuthChallenge,
189 Authenticate,
191 Authorize,
193}
194
195impl CellType {
196 pub fn name(&self) -> &'static str {
200 match self {
201 CellType::Padding => "PADDING",
202 CellType::Create => "CREATE",
203 CellType::Created => "CREATED",
204 CellType::Relay => "RELAY",
205 CellType::Destroy => "DESTROY",
206 CellType::CreateFast => "CREATE_FAST",
207 CellType::CreatedFast => "CREATED_FAST",
208 CellType::Versions => "VERSIONS",
209 CellType::Netinfo => "NETINFO",
210 CellType::RelayEarly => "RELAY_EARLY",
211 CellType::Create2 => "CREATE2",
212 CellType::Created2 => "CREATED2",
213 CellType::PaddingNegotiate => "PADDING_NEGOTIATE",
214 CellType::VPadding => "VPADDING",
215 CellType::Certs => "CERTS",
216 CellType::AuthChallenge => "AUTH_CHALLENGE",
217 CellType::Authenticate => "AUTHENTICATE",
218 CellType::Authorize => "AUTHORIZE",
219 }
220 }
221
222 pub fn value(&self) -> u8 {
226 match self {
227 CellType::Padding => 0,
228 CellType::Create => 1,
229 CellType::Created => 2,
230 CellType::Relay => 3,
231 CellType::Destroy => 4,
232 CellType::CreateFast => 5,
233 CellType::CreatedFast => 6,
234 CellType::Versions => 7,
235 CellType::Netinfo => 8,
236 CellType::RelayEarly => 9,
237 CellType::Create2 => 10,
238 CellType::Created2 => 11,
239 CellType::PaddingNegotiate => 12,
240 CellType::VPadding => 128,
241 CellType::Certs => 129,
242 CellType::AuthChallenge => 130,
243 CellType::Authenticate => 131,
244 CellType::Authorize => 132,
245 }
246 }
247
248 pub fn is_fixed_size(&self) -> bool {
253 !matches!(
254 self,
255 CellType::Versions
256 | CellType::VPadding
257 | CellType::Certs
258 | CellType::AuthChallenge
259 | CellType::Authenticate
260 | CellType::Authorize
261 )
262 }
263}
264
265pub fn cell_by_name(name: &str) -> Result<CellType, Error> {
289 match name {
290 "PADDING" => Ok(CellType::Padding),
291 "CREATE" => Ok(CellType::Create),
292 "CREATED" => Ok(CellType::Created),
293 "RELAY" => Ok(CellType::Relay),
294 "DESTROY" => Ok(CellType::Destroy),
295 "CREATE_FAST" => Ok(CellType::CreateFast),
296 "CREATED_FAST" => Ok(CellType::CreatedFast),
297 "VERSIONS" => Ok(CellType::Versions),
298 "NETINFO" => Ok(CellType::Netinfo),
299 "RELAY_EARLY" => Ok(CellType::RelayEarly),
300 "CREATE2" => Ok(CellType::Create2),
301 "CREATED2" => Ok(CellType::Created2),
302 "PADDING_NEGOTIATE" => Ok(CellType::PaddingNegotiate),
303 "VPADDING" => Ok(CellType::VPadding),
304 "CERTS" => Ok(CellType::Certs),
305 "AUTH_CHALLENGE" => Ok(CellType::AuthChallenge),
306 "AUTHENTICATE" => Ok(CellType::Authenticate),
307 "AUTHORIZE" => Ok(CellType::Authorize),
308 _ => Err(Error::Protocol(format!(
309 "'{}' isn't a valid cell type",
310 name
311 ))),
312 }
313}
314
315pub fn cell_by_value(value: u8) -> Result<CellType, Error> {
338 match value {
339 0 => Ok(CellType::Padding),
340 1 => Ok(CellType::Create),
341 2 => Ok(CellType::Created),
342 3 => Ok(CellType::Relay),
343 4 => Ok(CellType::Destroy),
344 5 => Ok(CellType::CreateFast),
345 6 => Ok(CellType::CreatedFast),
346 7 => Ok(CellType::Versions),
347 8 => Ok(CellType::Netinfo),
348 9 => Ok(CellType::RelayEarly),
349 10 => Ok(CellType::Create2),
350 11 => Ok(CellType::Created2),
351 12 => Ok(CellType::PaddingNegotiate),
352 128 => Ok(CellType::VPadding),
353 129 => Ok(CellType::Certs),
354 130 => Ok(CellType::AuthChallenge),
355 131 => Ok(CellType::Authenticate),
356 132 => Ok(CellType::Authorize),
357 _ => Err(Error::Protocol(format!(
358 "'{}' isn't a valid cell value",
359 value
360 ))),
361 }
362}
363
364#[derive(Debug, Clone, PartialEq)]
397pub enum Cell {
398 Padding(PaddingCell),
400 Versions(VersionsCell),
402 Netinfo(NetinfoCell),
404 CreateFast(CreateFastCell),
406 CreatedFast(CreatedFastCell),
408 Relay(RelayCell),
410 Destroy(DestroyCell),
412 VPadding(VPaddingCell),
414 Certs(CertsCell),
416 AuthChallenge(AuthChallengeCell),
418}
419
420impl Cell {
421 pub fn pack(&self, link_protocol: u32) -> Vec<u8> {
429 let lp = LinkProtocol::new(link_protocol);
430 match self {
431 Cell::Padding(c) => c.pack(&lp),
432 Cell::Versions(c) => c.pack(&lp),
433 Cell::Netinfo(c) => c.pack(&lp),
434 Cell::CreateFast(c) => c.pack(&lp),
435 Cell::CreatedFast(c) => c.pack(&lp),
436 Cell::Relay(c) => c.pack(&lp),
437 Cell::Destroy(c) => c.pack(&lp),
438 Cell::VPadding(c) => c.pack(&lp),
439 Cell::Certs(c) => c.pack(&lp),
440 Cell::AuthChallenge(c) => c.pack(&lp),
441 }
442 }
443
444 pub fn pop(content: &[u8], link_protocol: u32) -> Result<(Cell, Vec<u8>), Error> {
479 let lp = LinkProtocol::new(link_protocol);
480 let circ_id_size = lp.circ_id_size.size();
481
482 if content.len() < circ_id_size + CELL_TYPE_SIZE.size() {
483 return Err(Error::Protocol(
484 "Cell content too short for header".to_string(),
485 ));
486 }
487
488 let (circ_id, rest) = lp.circ_id_size.pop(content)?;
489 let (command, rest) = CELL_TYPE_SIZE.pop(rest)?;
490 let cell_type = cell_by_value(command as u8)?;
491
492 let payload_len = if cell_type.is_fixed_size() {
493 FIXED_PAYLOAD_LEN
494 } else {
495 if rest.len() < PAYLOAD_LEN_SIZE.size() {
496 return Err(Error::Protocol(
497 "Cell content too short for payload length".to_string(),
498 ));
499 }
500 let (len, _) = PAYLOAD_LEN_SIZE.pop(rest)?;
501 len as usize
502 };
503
504 let header_size = if cell_type.is_fixed_size() {
505 circ_id_size + CELL_TYPE_SIZE.size()
506 } else {
507 circ_id_size + CELL_TYPE_SIZE.size() + PAYLOAD_LEN_SIZE.size()
508 };
509
510 let total_size = header_size + payload_len;
511 if content.len() < total_size {
512 return Err(Error::Protocol(format!(
513 "{} cell should have a payload of {} bytes, but only had {}",
514 cell_type.name(),
515 payload_len,
516 content.len() - header_size
517 )));
518 }
519
520 let (_, payload_start) = split(content, header_size);
521 let (payload, _) = split(payload_start, payload_len);
522 let remainder = content[total_size..].to_vec();
523
524 let cell = match cell_type {
525 CellType::Padding => Cell::Padding(PaddingCell::unpack(payload)?),
526 CellType::Versions => Cell::Versions(VersionsCell::unpack(payload)?),
527 CellType::Netinfo => Cell::Netinfo(NetinfoCell::unpack(payload)?),
528 CellType::CreateFast => {
529 Cell::CreateFast(CreateFastCell::unpack(payload, circ_id as u32)?)
530 }
531 CellType::CreatedFast => {
532 Cell::CreatedFast(CreatedFastCell::unpack(payload, circ_id as u32)?)
533 }
534 CellType::Relay => Cell::Relay(RelayCell::unpack(payload, circ_id as u32)?),
535 CellType::Destroy => Cell::Destroy(DestroyCell::unpack(payload, circ_id as u32)?),
536 CellType::VPadding => Cell::VPadding(VPaddingCell::unpack(payload)?),
537 CellType::Certs => Cell::Certs(CertsCell::unpack(payload)?),
538 CellType::AuthChallenge => Cell::AuthChallenge(AuthChallengeCell::unpack(payload)?),
539 _ => {
540 return Err(Error::Protocol(format!(
541 "Unpacking not yet implemented for {} cells",
542 cell_type.name()
543 )))
544 }
545 };
546
547 Ok((cell, remainder))
548 }
549
550 pub fn unpack_all(content: &[u8], link_protocol: u32) -> Result<Vec<Cell>, Error> {
563 let mut cells = Vec::new();
564 let mut remaining = content.to_vec();
565 while !remaining.is_empty() {
566 let (cell, rest) = Cell::pop(&remaining, link_protocol)?;
567 cells.push(cell);
568 remaining = rest;
569 }
570 Ok(cells)
571 }
572}
573
574#[derive(Debug, Clone, PartialEq)]
600pub struct PaddingCell {
601 pub payload: Vec<u8>,
603}
604
605impl PaddingCell {
606 pub fn new() -> Self {
610 let mut payload = vec![0u8; FIXED_PAYLOAD_LEN];
611 getrandom::fill(&mut payload).expect("Failed to generate random bytes");
612 PaddingCell { payload }
613 }
614
615 pub fn with_payload(payload: Vec<u8>) -> Result<Self, Error> {
625 if payload.len() != FIXED_PAYLOAD_LEN {
626 return Err(Error::Protocol(format!(
627 "Padding payload should be {} bytes, but was {}",
628 FIXED_PAYLOAD_LEN,
629 payload.len()
630 )));
631 }
632 Ok(PaddingCell { payload })
633 }
634
635 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
637 pack_fixed_cell(
638 link_protocol,
639 CellType::Padding.value(),
640 &self.payload,
641 None,
642 )
643 }
644
645 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
647 Ok(PaddingCell {
648 payload: payload.to_vec(),
649 })
650 }
651}
652
653impl Default for PaddingCell {
654 fn default() -> Self {
655 Self::new()
656 }
657}
658
659#[derive(Debug, Clone, PartialEq)]
688pub struct VersionsCell {
689 pub versions: Vec<u32>,
691}
692
693impl VersionsCell {
694 pub fn new(versions: Vec<u32>) -> Self {
700 VersionsCell { versions }
701 }
702
703 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
705 let payload: Vec<u8> = self
706 .versions
707 .iter()
708 .flat_map(|v| Size::Short.pack(*v as u64))
709 .collect();
710 pack_variable_cell(link_protocol, CellType::Versions.value(), &payload, None)
711 }
712
713 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
715 let mut versions = Vec::new();
716 let mut content = payload;
717 while !content.is_empty() {
718 let (version, rest) = Size::Short.pop(content)?;
719 versions.push(version as u32);
720 content = rest;
721 }
722 Ok(VersionsCell { versions })
723 }
724}
725
726#[derive(Debug, Clone, PartialEq)]
749pub struct NetinfoCell {
750 pub timestamp: DateTime<Utc>,
752 pub receiver_address: Address,
754 pub sender_addresses: Vec<Address>,
756 pub unused: Vec<u8>,
758}
759
760impl NetinfoCell {
761 pub fn new(
769 receiver_address: Address,
770 sender_addresses: Vec<Address>,
771 timestamp: Option<DateTime<Utc>>,
772 ) -> Self {
773 NetinfoCell {
774 timestamp: timestamp.unwrap_or_else(Utc::now),
775 receiver_address,
776 sender_addresses,
777 unused: Vec::new(),
778 }
779 }
780
781 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
783 let mut payload = Vec::new();
784 payload.extend_from_slice(&Size::Long.pack(self.timestamp.timestamp() as u64));
785 payload.extend_from_slice(&self.receiver_address.pack());
786 payload.push(self.sender_addresses.len() as u8);
787 for addr in &self.sender_addresses {
788 payload.extend_from_slice(&addr.pack());
789 }
790 pack_fixed_cell(
791 link_protocol,
792 CellType::Netinfo.value(),
793 &payload,
794 Some(&self.unused),
795 )
796 }
797
798 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
800 let (timestamp, content) = Size::Long.pop(payload)?;
801 let (receiver_address, content) = Address::pop(content)?;
802 let (sender_addr_count, mut content) = Size::Char.pop(content)?;
803
804 let mut sender_addresses = Vec::new();
805 for _ in 0..sender_addr_count {
806 let (addr, rest) = Address::pop(content)?;
807 sender_addresses.push(addr);
808 content = rest;
809 }
810
811 Ok(NetinfoCell {
812 timestamp: Utc.timestamp_opt(timestamp as i64, 0).unwrap(),
813 receiver_address,
814 sender_addresses,
815 unused: content.to_vec(),
816 })
817 }
818}
819
820#[derive(Debug, Clone, PartialEq)]
847pub struct CreateFastCell {
848 pub circ_id: u32,
850 pub key_material: [u8; HASH_LEN],
852 pub unused: Vec<u8>,
854}
855
856impl CreateFastCell {
857 pub fn new(circ_id: u32) -> Self {
863 let mut key_material = [0u8; HASH_LEN];
864 getrandom::fill(&mut key_material).expect("Failed to generate random bytes");
865 CreateFastCell {
866 circ_id,
867 key_material,
868 unused: Vec::new(),
869 }
870 }
871
872 pub fn with_key_material(circ_id: u32, key_material: [u8; HASH_LEN]) -> Self {
879 CreateFastCell {
880 circ_id,
881 key_material,
882 unused: Vec::new(),
883 }
884 }
885
886 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
888 pack_fixed_cell(
889 link_protocol,
890 CellType::CreateFast.value(),
891 &self.key_material,
892 Some(&self.unused),
893 )
894 .iter()
895 .enumerate()
896 .map(|(i, &b)| {
897 if i < link_protocol.circ_id_size.size() {
898 let circ_id_bytes = if link_protocol.circ_id_size == Size::Long {
899 self.circ_id.to_be_bytes().to_vec()
900 } else {
901 (self.circ_id as u16).to_be_bytes().to_vec()
902 };
903 circ_id_bytes.get(i).copied().unwrap_or(b)
904 } else {
905 b
906 }
907 })
908 .collect()
909 }
910
911 pub fn unpack(payload: &[u8], circ_id: u32) -> Result<Self, Error> {
917 if payload.len() < HASH_LEN {
918 return Err(Error::Protocol(format!(
919 "Key material should be {} bytes, but was {}",
920 HASH_LEN,
921 payload.len()
922 )));
923 }
924 let (key_material_slice, unused) = split(payload, HASH_LEN);
925 let mut key_material = [0u8; HASH_LEN];
926 key_material.copy_from_slice(key_material_slice);
927
928 Ok(CreateFastCell {
929 circ_id,
930 key_material,
931 unused: unused.to_vec(),
932 })
933 }
934}
935
936#[derive(Debug, Clone, PartialEq)]
953pub struct CreatedFastCell {
954 pub circ_id: u32,
956 pub key_material: [u8; HASH_LEN],
958 pub derivative_key: [u8; HASH_LEN],
960 pub unused: Vec<u8>,
962}
963
964impl CreatedFastCell {
965 pub fn new(circ_id: u32, derivative_key: [u8; HASH_LEN]) -> Self {
972 let mut key_material = [0u8; HASH_LEN];
973 getrandom::fill(&mut key_material).expect("Failed to generate random bytes");
974 CreatedFastCell {
975 circ_id,
976 key_material,
977 derivative_key,
978 unused: Vec::new(),
979 }
980 }
981
982 pub fn with_key_material(
990 circ_id: u32,
991 key_material: [u8; HASH_LEN],
992 derivative_key: [u8; HASH_LEN],
993 ) -> Self {
994 CreatedFastCell {
995 circ_id,
996 key_material,
997 derivative_key,
998 unused: Vec::new(),
999 }
1000 }
1001
1002 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1004 let mut payload = Vec::new();
1005 payload.extend_from_slice(&self.key_material);
1006 payload.extend_from_slice(&self.derivative_key);
1007 let mut cell = pack_fixed_cell(
1008 link_protocol,
1009 CellType::CreatedFast.value(),
1010 &payload,
1011 Some(&self.unused),
1012 );
1013 let circ_id_bytes = if link_protocol.circ_id_size == Size::Long {
1014 self.circ_id.to_be_bytes().to_vec()
1015 } else {
1016 (self.circ_id as u16).to_be_bytes().to_vec()
1017 };
1018 for (i, &b) in circ_id_bytes.iter().enumerate() {
1019 cell[i] = b;
1020 }
1021 cell
1022 }
1023
1024 pub fn unpack(payload: &[u8], circ_id: u32) -> Result<Self, Error> {
1030 if payload.len() < HASH_LEN * 2 {
1031 return Err(Error::Protocol(format!(
1032 "Key material and derivative key should be {} bytes, but was {}",
1033 HASH_LEN * 2,
1034 payload.len()
1035 )));
1036 }
1037 let (key_material_slice, rest) = split(payload, HASH_LEN);
1038 let (derivative_key_slice, unused) = split(rest, HASH_LEN);
1039
1040 let mut key_material = [0u8; HASH_LEN];
1041 let mut derivative_key = [0u8; HASH_LEN];
1042 key_material.copy_from_slice(key_material_slice);
1043 derivative_key.copy_from_slice(derivative_key_slice);
1044
1045 Ok(CreatedFastCell {
1046 circ_id,
1047 key_material,
1048 derivative_key,
1049 unused: unused.to_vec(),
1050 })
1051 }
1052}
1053
1054#[derive(Debug, Clone, PartialEq)]
1080pub struct RelayCell {
1081 pub circ_id: u32,
1083 pub command: RelayCommand,
1085 pub command_int: u8,
1087 pub recognized: u16,
1089 pub stream_id: u16,
1091 pub digest: u32,
1093 pub data: Vec<u8>,
1095 pub unused: Vec<u8>,
1097}
1098
1099impl RelayCell {
1100 pub fn new(
1116 circ_id: u32,
1117 command: RelayCommand,
1118 data: Vec<u8>,
1119 digest: u32,
1120 stream_id: u16,
1121 ) -> Result<Self, Error> {
1122 if digest == 0 {
1123 if stream_id == 0 && STREAM_ID_REQUIRED.contains(&command) {
1124 return Err(Error::Protocol(format!(
1125 "{} relay cells require a stream id",
1126 command
1127 )));
1128 }
1129 if stream_id != 0 && STREAM_ID_DISALLOWED.contains(&command) {
1130 return Err(Error::Protocol(format!(
1131 "{} relay cells concern the circuit itself and cannot have a stream id",
1132 command
1133 )));
1134 }
1135 }
1136
1137 Ok(RelayCell {
1138 circ_id,
1139 command_int: command.value(),
1140 command,
1141 recognized: 0,
1142 stream_id,
1143 digest,
1144 data,
1145 unused: Vec::new(),
1146 })
1147 }
1148
1149 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1151 let mut payload = Vec::new();
1152 payload.push(self.command_int);
1153 payload.extend_from_slice(&self.recognized.to_be_bytes());
1154 payload.extend_from_slice(&self.stream_id.to_be_bytes());
1155 payload.extend_from_slice(&self.digest.to_be_bytes());
1156 payload.extend_from_slice(&(self.data.len() as u16).to_be_bytes());
1157 payload.extend_from_slice(&self.data);
1158
1159 let mut cell = pack_fixed_cell(
1160 link_protocol,
1161 CellType::Relay.value(),
1162 &payload,
1163 Some(&self.unused),
1164 );
1165 let circ_id_bytes = if link_protocol.circ_id_size == Size::Long {
1166 self.circ_id.to_be_bytes().to_vec()
1167 } else {
1168 (self.circ_id as u16).to_be_bytes().to_vec()
1169 };
1170 for (i, &b) in circ_id_bytes.iter().enumerate() {
1171 cell[i] = b;
1172 }
1173 cell
1174 }
1175
1176 pub fn unpack(payload: &[u8], circ_id: u32) -> Result<Self, Error> {
1182 let (command, content) = Size::Char.pop(payload)?;
1183 let (recognized, content) = Size::Short.pop(content)?;
1184 let (stream_id, content) = Size::Short.pop(content)?;
1185 let (digest, content) = Size::Long.pop(content)?;
1186 let (data_len, content) = Size::Short.pop(content)?;
1187 let data_len = data_len as usize;
1188
1189 if content.len() < data_len {
1190 return Err(Error::Protocol(format!(
1191 "RELAY cell said it had {} bytes of data, but only had {}",
1192 data_len,
1193 content.len()
1194 )));
1195 }
1196
1197 let (data, unused) = split(content, data_len);
1198 let (cmd, cmd_int) = RelayCommand::get(command as u8);
1199
1200 Ok(RelayCell {
1201 circ_id,
1202 command: cmd,
1203 command_int: cmd_int,
1204 recognized: recognized as u16,
1205 stream_id: stream_id as u16,
1206 digest: digest as u32,
1207 data: data.to_vec(),
1208 unused: unused.to_vec(),
1209 })
1210 }
1211}
1212
1213#[derive(Debug, Clone, PartialEq)]
1233pub struct DestroyCell {
1234 pub circ_id: u32,
1236 pub reason: CloseReason,
1238 pub reason_int: u8,
1240 pub unused: Vec<u8>,
1242}
1243
1244impl DestroyCell {
1245 pub fn new(circ_id: u32, reason: CloseReason) -> Self {
1252 DestroyCell {
1253 circ_id,
1254 reason_int: reason.value(),
1255 reason,
1256 unused: Vec::new(),
1257 }
1258 }
1259
1260 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1262 let payload = vec![self.reason_int];
1263 let mut cell = pack_fixed_cell(
1264 link_protocol,
1265 CellType::Destroy.value(),
1266 &payload,
1267 Some(&self.unused),
1268 );
1269 let circ_id_bytes = if link_protocol.circ_id_size == Size::Long {
1270 self.circ_id.to_be_bytes().to_vec()
1271 } else {
1272 (self.circ_id as u16).to_be_bytes().to_vec()
1273 };
1274 for (i, &b) in circ_id_bytes.iter().enumerate() {
1275 cell[i] = b;
1276 }
1277 cell
1278 }
1279
1280 pub fn unpack(payload: &[u8], circ_id: u32) -> Result<Self, Error> {
1282 let (reason, unused) = Size::Char.pop(payload)?;
1283 let (close_reason, reason_int) = CloseReason::get(reason as u8);
1284
1285 Ok(DestroyCell {
1286 circ_id,
1287 reason: close_reason,
1288 reason_int,
1289 unused: unused.to_vec(),
1290 })
1291 }
1292}
1293
1294#[derive(Debug, Clone, PartialEq)]
1307pub struct VPaddingCell {
1308 pub payload: Vec<u8>,
1310}
1311
1312impl VPaddingCell {
1313 pub fn new(size: usize) -> Self {
1319 let mut payload = vec![0u8; size];
1320 if size > 0 {
1321 getrandom::fill(&mut payload).expect("Failed to generate random bytes");
1322 }
1323 VPaddingCell { payload }
1324 }
1325
1326 pub fn with_payload(payload: Vec<u8>) -> Self {
1332 VPaddingCell { payload }
1333 }
1334
1335 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1337 pack_variable_cell(
1338 link_protocol,
1339 CellType::VPadding.value(),
1340 &self.payload,
1341 None,
1342 )
1343 }
1344
1345 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
1347 Ok(VPaddingCell {
1348 payload: payload.to_vec(),
1349 })
1350 }
1351}
1352
1353#[derive(Debug, Clone, PartialEq)]
1370pub struct CertsCell {
1371 pub certificates: Vec<Certificate>,
1373 pub unused: Vec<u8>,
1375}
1376
1377impl CertsCell {
1378 pub fn new(certificates: Vec<Certificate>) -> Self {
1384 CertsCell {
1385 certificates,
1386 unused: Vec::new(),
1387 }
1388 }
1389
1390 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1392 let mut payload = Vec::new();
1393 payload.push(self.certificates.len() as u8);
1394 for cert in &self.certificates {
1395 payload.extend_from_slice(&cert.pack());
1396 }
1397 payload.extend_from_slice(&self.unused);
1398 pack_variable_cell(link_protocol, CellType::Certs.value(), &payload, None)
1399 }
1400
1401 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
1408 if payload.is_empty() {
1409 return Ok(CertsCell {
1410 certificates: Vec::new(),
1411 unused: Vec::new(),
1412 });
1413 }
1414
1415 let (cert_count, mut content) = Size::Char.pop(payload)?;
1416 let mut certificates = Vec::new();
1417
1418 for _ in 0..cert_count {
1419 if content.is_empty() {
1420 return Err(Error::Protocol(format!(
1421 "CERTS cell indicates it should have {} certificates, but only contained {}",
1422 cert_count,
1423 certificates.len()
1424 )));
1425 }
1426 let (cert, rest) = Certificate::pop(content)?;
1427 certificates.push(cert);
1428 content = rest;
1429 }
1430
1431 Ok(CertsCell {
1432 certificates,
1433 unused: content.to_vec(),
1434 })
1435 }
1436}
1437
1438#[derive(Debug, Clone, PartialEq)]
1452pub struct AuthChallengeCell {
1453 pub challenge: [u8; AUTH_CHALLENGE_SIZE],
1455 pub methods: Vec<u16>,
1457 pub unused: Vec<u8>,
1459}
1460
1461impl AuthChallengeCell {
1462 pub fn new(methods: Vec<u16>) -> Self {
1468 let mut challenge = [0u8; AUTH_CHALLENGE_SIZE];
1469 getrandom::fill(&mut challenge).expect("Failed to generate random bytes");
1470 AuthChallengeCell {
1471 challenge,
1472 methods,
1473 unused: Vec::new(),
1474 }
1475 }
1476
1477 pub fn with_challenge(challenge: [u8; AUTH_CHALLENGE_SIZE], methods: Vec<u16>) -> Self {
1484 AuthChallengeCell {
1485 challenge,
1486 methods,
1487 unused: Vec::new(),
1488 }
1489 }
1490
1491 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1493 let mut payload = Vec::new();
1494 payload.extend_from_slice(&self.challenge);
1495 payload.extend_from_slice(&(self.methods.len() as u16).to_be_bytes());
1496 for method in &self.methods {
1497 payload.extend_from_slice(&method.to_be_bytes());
1498 }
1499 payload.extend_from_slice(&self.unused);
1500 pack_variable_cell(
1501 link_protocol,
1502 CellType::AuthChallenge.value(),
1503 &payload,
1504 None,
1505 )
1506 }
1507
1508 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
1515 let min_size = AUTH_CHALLENGE_SIZE + Size::Short.size();
1516 if payload.len() < min_size {
1517 return Err(Error::Protocol(format!(
1518 "AUTH_CHALLENGE payload should be at least {} bytes, but was {}",
1519 min_size,
1520 payload.len()
1521 )));
1522 }
1523
1524 let (challenge_slice, content) = split(payload, AUTH_CHALLENGE_SIZE);
1525 let (method_count, mut content) = Size::Short.pop(content)?;
1526
1527 if content.len() < (method_count as usize) * Size::Short.size() {
1528 return Err(Error::Protocol(format!(
1529 "AUTH_CHALLENGE should have {} methods, but only had {} bytes for it",
1530 method_count,
1531 content.len()
1532 )));
1533 }
1534
1535 let mut methods = Vec::new();
1536 for _ in 0..method_count {
1537 let (method, rest) = Size::Short.pop(content)?;
1538 methods.push(method as u16);
1539 content = rest;
1540 }
1541
1542 let mut challenge = [0u8; AUTH_CHALLENGE_SIZE];
1543 challenge.copy_from_slice(challenge_slice);
1544
1545 Ok(AuthChallengeCell {
1546 challenge,
1547 methods,
1548 unused: content.to_vec(),
1549 })
1550 }
1551}
1552
1553fn pack_fixed_cell(
1564 link_protocol: &LinkProtocol,
1565 command: u8,
1566 payload: &[u8],
1567 unused: Option<&[u8]>,
1568) -> Vec<u8> {
1569 let mut cell = Vec::new();
1570
1571 cell.extend_from_slice(&vec![0u8; link_protocol.circ_id_size.size()]);
1572
1573 cell.push(command);
1574
1575 cell.extend_from_slice(payload);
1576
1577 if let Some(unused_bytes) = unused {
1578 cell.extend_from_slice(unused_bytes);
1579 }
1580
1581 let padding_needed = link_protocol.fixed_cell_length.saturating_sub(cell.len());
1582 cell.extend(std::iter::repeat_n(ZERO, padding_needed));
1583
1584 cell
1585}
1586
1587fn pack_variable_cell(
1598 link_protocol: &LinkProtocol,
1599 command: u8,
1600 payload: &[u8],
1601 _unused: Option<&[u8]>,
1602) -> Vec<u8> {
1603 let mut cell = Vec::new();
1604
1605 cell.extend_from_slice(&vec![0u8; link_protocol.circ_id_size.size()]);
1606
1607 cell.push(command);
1608
1609 cell.extend_from_slice(&(payload.len() as u16).to_be_bytes());
1610
1611 cell.extend_from_slice(payload);
1612
1613 cell
1614}
1615
1616#[cfg(test)]
1617mod tests {
1618 use super::*;
1619
1620 #[test]
1621 fn test_cell_by_name() {
1622 let cell_type = cell_by_name("NETINFO").unwrap();
1623 assert_eq!("NETINFO", cell_type.name());
1624 assert_eq!(8, cell_type.value());
1625 assert!(cell_type.is_fixed_size());
1626
1627 assert!(cell_by_name("NOPE").is_err());
1628 }
1629
1630 #[test]
1631 fn test_cell_by_value() {
1632 let cell_type = cell_by_value(8).unwrap();
1633 assert_eq!("NETINFO", cell_type.name());
1634 assert_eq!(8, cell_type.value());
1635 assert!(cell_type.is_fixed_size());
1636
1637 assert!(cell_by_value(85).is_err());
1638 }
1639
1640 #[test]
1641 fn test_versions_cell() {
1642 let versions = vec![1, 2, 3];
1643 let cell = VersionsCell::new(versions.clone());
1644 let packed = cell.pack(&LinkProtocol::new(2));
1645
1646 let expected = b"\x00\x00\x07\x00\x06\x00\x01\x00\x02\x00\x03";
1647 assert_eq!(expected.to_vec(), packed);
1648
1649 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1650 match unpacked {
1651 Cell::Versions(v) => assert_eq!(versions, v.versions),
1652 _ => panic!("Expected VersionsCell"),
1653 }
1654 }
1655
1656 #[test]
1657 fn test_versions_cell_empty() {
1658 let cell = VersionsCell::new(vec![]);
1659 let packed = cell.pack(&LinkProtocol::new(2));
1660
1661 let expected = b"\x00\x00\x07\x00\x00";
1662 assert_eq!(expected.to_vec(), packed);
1663 }
1664
1665 #[test]
1666 fn test_vpadding_cell() {
1667 let cell = VPaddingCell::with_payload(vec![]);
1668 let packed = cell.pack(&LinkProtocol::new(2));
1669
1670 let expected = b"\x00\x00\x80\x00\x00";
1671 assert_eq!(expected.to_vec(), packed);
1672
1673 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1674 match unpacked {
1675 Cell::VPadding(v) => assert!(v.payload.is_empty()),
1676 _ => panic!("Expected VPaddingCell"),
1677 }
1678 }
1679
1680 #[test]
1681 fn test_vpadding_cell_with_data() {
1682 let cell = VPaddingCell::with_payload(vec![0x08, 0x11]);
1683 let packed = cell.pack(&LinkProtocol::new(2));
1684
1685 let expected = b"\x00\x00\x80\x00\x02\x08\x11";
1686 assert_eq!(expected.to_vec(), packed);
1687 }
1688
1689 #[test]
1690 fn test_destroy_cell() {
1691 let cell = DestroyCell::new(2147483648, CloseReason::None);
1692 let packed = cell.pack(&LinkProtocol::new(5));
1693
1694 assert_eq!(0x80, packed[0]);
1695 assert_eq!(0x00, packed[1]);
1696 assert_eq!(0x00, packed[2]);
1697 assert_eq!(0x00, packed[3]);
1698 assert_eq!(4, packed[4]);
1699 assert_eq!(0, packed[5]);
1700
1701 let (unpacked, _) = Cell::pop(&packed, 5).unwrap();
1702 match unpacked {
1703 Cell::Destroy(d) => {
1704 assert_eq!(2147483648, d.circ_id);
1705 assert_eq!(CloseReason::None, d.reason);
1706 }
1707 _ => panic!("Expected DestroyCell"),
1708 }
1709 }
1710
1711 #[test]
1712 fn test_create_fast_cell() {
1713 let key_material: [u8; HASH_LEN] = [
1714 0x92, 0x4f, 0x0c, 0xcb, 0xa8, 0xac, 0xfb, 0xc9, 0x7f, 0xd0, 0x0d, 0x7a, 0x1a, 0x03,
1715 0x75, 0x91, 0xce, 0x61, 0x73, 0xce,
1716 ];
1717 let cell = CreateFastCell::with_key_material(2147483648, key_material);
1718 let packed = cell.pack(&LinkProtocol::new(5));
1719
1720 assert_eq!(0x80, packed[0]);
1721 assert_eq!(0x00, packed[1]);
1722 assert_eq!(0x00, packed[2]);
1723 assert_eq!(0x00, packed[3]);
1724 assert_eq!(5, packed[4]);
1725
1726 let (unpacked, _) = Cell::pop(&packed, 5).unwrap();
1727 match unpacked {
1728 Cell::CreateFast(c) => {
1729 assert_eq!(2147483648, c.circ_id);
1730 assert_eq!(key_material, c.key_material);
1731 }
1732 _ => panic!("Expected CreateFastCell"),
1733 }
1734 }
1735
1736 #[test]
1737 fn test_created_fast_cell() {
1738 let key_material: [u8; HASH_LEN] = [
1739 0x92, 0x4f, 0x0c, 0xcb, 0xa8, 0xac, 0xfb, 0xc9, 0x7f, 0xd0, 0x0d, 0x7a, 0x1a, 0x03,
1740 0x75, 0x91, 0xce, 0x61, 0x73, 0xce,
1741 ];
1742 let derivative_key: [u8; HASH_LEN] = [
1743 0x13, 0x5a, 0x99, 0xb2, 0x1e, 0xb6, 0x05, 0x85, 0x17, 0xfc, 0x1c, 0x00, 0x7b, 0xa9,
1744 0xae, 0x83, 0x5e, 0x4b, 0x99, 0xb2,
1745 ];
1746 let cell = CreatedFastCell::with_key_material(2147483648, key_material, derivative_key);
1747 let packed = cell.pack(&LinkProtocol::new(5));
1748
1749 let (unpacked, _) = Cell::pop(&packed, 5).unwrap();
1750 match unpacked {
1751 Cell::CreatedFast(c) => {
1752 assert_eq!(2147483648, c.circ_id);
1753 assert_eq!(key_material, c.key_material);
1754 assert_eq!(derivative_key, c.derivative_key);
1755 }
1756 _ => panic!("Expected CreatedFastCell"),
1757 }
1758 }
1759
1760 #[test]
1761 fn test_relay_cell() {
1762 let cell = RelayCell::new(1, RelayCommand::BeginDir, vec![], 564346860, 1).unwrap();
1763 let packed = cell.pack(&LinkProtocol::new(2));
1764
1765 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1766 match unpacked {
1767 Cell::Relay(r) => {
1768 assert_eq!(1, r.circ_id);
1769 assert_eq!(RelayCommand::BeginDir, r.command);
1770 assert_eq!(564346860, r.digest);
1771 assert_eq!(1, r.stream_id);
1772 }
1773 _ => panic!("Expected RelayCell"),
1774 }
1775 }
1776
1777 #[test]
1778 fn test_certs_cell_empty() {
1779 let cell = CertsCell::new(vec![]);
1780 let packed = cell.pack(&LinkProtocol::new(2));
1781
1782 let expected = b"\x00\x00\x81\x00\x01\x00";
1783 assert_eq!(expected.to_vec(), packed);
1784 }
1785
1786 #[test]
1787 fn test_certs_cell_with_cert() {
1788 let cert = Certificate::from_int(1, vec![]);
1789 let cell = CertsCell::new(vec![cert]);
1790 let packed = cell.pack(&LinkProtocol::new(2));
1791
1792 let expected = b"\x00\x00\x81\x00\x04\x01\x01\x00\x00";
1793 assert_eq!(expected.to_vec(), packed);
1794 }
1795
1796 #[test]
1797 fn test_auth_challenge_cell() {
1798 let challenge: [u8; AUTH_CHALLENGE_SIZE] = [
1799 0x89, 0x59, 0x09, 0x99, 0xb2, 0x1e, 0xd9, 0x2a, 0x56, 0xb6, 0x1b, 0x6e, 0x0a, 0x05,
1800 0xd8, 0x2f, 0xe3, 0x51, 0x48, 0x85, 0x13, 0x5a, 0x17, 0xfc, 0x1c, 0x00, 0x7b, 0xa9,
1801 0xae, 0x83, 0x5e, 0x4b,
1802 ];
1803 let cell = AuthChallengeCell::with_challenge(challenge, vec![1, 3]);
1804 let packed = cell.pack(&LinkProtocol::new(2));
1805
1806 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1807 match unpacked {
1808 Cell::AuthChallenge(a) => {
1809 assert_eq!(challenge, a.challenge);
1810 assert_eq!(vec![1, 3], a.methods);
1811 }
1812 _ => panic!("Expected AuthChallengeCell"),
1813 }
1814 }
1815
1816 #[test]
1817 fn test_netinfo_cell() {
1818 use chrono::TimeZone;
1819 let timestamp = Utc.with_ymd_and_hms(2018, 1, 14, 1, 46, 56).unwrap();
1820 let receiver = Address::new("127.0.0.1").unwrap();
1821 let sender = Address::new("97.113.15.2").unwrap();
1822
1823 let cell = NetinfoCell::new(receiver.clone(), vec![sender.clone()], Some(timestamp));
1824 let packed = cell.pack(&LinkProtocol::new(2));
1825
1826 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1827 match unpacked {
1828 Cell::Netinfo(n) => {
1829 assert_eq!(timestamp, n.timestamp);
1830 assert_eq!(receiver, n.receiver_address);
1831 assert_eq!(vec![sender], n.sender_addresses);
1832 }
1833 _ => panic!("Expected NetinfoCell"),
1834 }
1835 }
1836
1837 #[test]
1838 fn test_relay_cell_with_data() {
1839 let data = b"GET /tor/server/authority HTTP/1.0\r\n\r\n";
1840 let cell = RelayCell::new(1, RelayCommand::Data, data.to_vec(), 356150752, 1).unwrap();
1841 let packed = cell.pack(&LinkProtocol::new(2));
1842
1843 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1844 match unpacked {
1845 Cell::Relay(r) => {
1846 assert_eq!(1, r.circ_id);
1847 assert_eq!(RelayCommand::Data, r.command);
1848 assert_eq!(2, r.command_int);
1849 assert_eq!(data.to_vec(), r.data);
1850 assert_eq!(356150752, r.digest);
1851 assert_eq!(1, r.stream_id);
1852 }
1853 _ => panic!("Expected RelayCell"),
1854 }
1855 }
1856
1857 #[test]
1858 fn test_relay_cell_stream_id_required() {
1859 let result = RelayCell::new(1, RelayCommand::BeginDir, vec![], 0, 0);
1860 assert!(result.is_err());
1861 }
1862
1863 #[test]
1864 fn test_relay_cell_stream_id_disallowed() {
1865 let result = RelayCell::new(1, RelayCommand::Extend, vec![], 0, 1);
1866 assert!(result.is_err());
1867 }
1868
1869 #[test]
1870 fn test_relay_cell_mismatched_data_length() {
1871 let mismatched_data = [
1872 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x15, 0x3a, 0x6d, 0xe0, 0xFF, 0xFF,
1873 ];
1874 let mut cell_bytes = mismatched_data.to_vec();
1875 cell_bytes.extend(vec![0u8; 498]);
1876
1877 let result = Cell::pop(&cell_bytes, 2);
1878 assert!(result.is_err());
1879 }
1880
1881 #[test]
1882 fn test_versions_cell_protocol_4() {
1883 let versions = vec![1, 2, 3, 4];
1884 let cell = VersionsCell::new(versions.clone());
1885 let packed = cell.pack(&LinkProtocol::new(4));
1886
1887 let expected = b"\x00\x00\x00\x00\x07\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04";
1888 assert_eq!(expected.to_vec(), packed);
1889
1890 let (unpacked, _) = Cell::pop(&packed, 4).unwrap();
1891 match unpacked {
1892 Cell::Versions(v) => assert_eq!(versions, v.versions),
1893 _ => panic!("Expected VersionsCell"),
1894 }
1895 }
1896
1897 #[test]
1898 fn test_certs_cell_truncated() {
1899 let truncated = b"\x00\x00\x81\x00\x05\x02\x01\x00\x01\x08";
1900 let result = Cell::pop(truncated, 2);
1901 assert!(result.is_err());
1902 }
1903
1904 #[test]
1905 fn test_certs_cell_cert_too_short() {
1906 let short_cert = b"\x00\x00\x81\x00\x05\x01\x01\x00\x03\x08";
1907 let result = Cell::pop(short_cert, 2);
1908 assert!(result.is_err());
1909 }
1910
1911 #[test]
1912 fn test_auth_challenge_cell_truncated() {
1913 let challenge: [u8; 32] = [
1914 0x89, 0x59, 0x09, 0x99, 0xb2, 0x1e, 0xd9, 0x2a, 0x56, 0xb6, 0x1b, 0x6e, 0x0a, 0x05,
1915 0xd8, 0x2f, 0xe3, 0x51, 0x48, 0x85, 0x13, 0x5a, 0x17, 0xfc, 0x1c, 0x00, 0x7b, 0xa9,
1916 0xae, 0x83, 0x5e, 0x4b,
1917 ];
1918 let mut truncated = vec![0x00, 0x00, 0x82, 0x00, 0x26];
1919 truncated.extend_from_slice(&challenge[..10]);
1920 truncated.extend_from_slice(&[0x00, 0x02, 0x00, 0x01, 0x00, 0x03]);
1921
1922 let result = Cell::pop(&truncated, 2);
1923 assert!(result.is_err());
1924 }
1925
1926 #[test]
1927 fn test_auth_challenge_cell_methods_truncated() {
1928 let challenge: [u8; 32] = [
1929 0x89, 0x59, 0x09, 0x99, 0xb2, 0x1e, 0xd9, 0x2a, 0x56, 0xb6, 0x1b, 0x6e, 0x0a, 0x05,
1930 0xd8, 0x2f, 0xe3, 0x51, 0x48, 0x85, 0x13, 0x5a, 0x17, 0xfc, 0x1c, 0x00, 0x7b, 0xa9,
1931 0xae, 0x83, 0x5e, 0x4b,
1932 ];
1933 let mut truncated = vec![0x00, 0x00, 0x82, 0x00, 0x26];
1934 truncated.extend_from_slice(&challenge);
1935 truncated.extend_from_slice(&[0x00, 0x03, 0x00, 0x01, 0x00, 0x03]);
1936
1937 let result = Cell::pop(&truncated, 2);
1938 assert!(result.is_err());
1939 }
1940
1941 #[test]
1942 fn test_padding_cell_roundtrip() {
1943 let payload = vec![0x42u8; FIXED_PAYLOAD_LEN];
1944 let cell = PaddingCell::with_payload(payload.clone()).unwrap();
1945 let packed = cell.pack(&LinkProtocol::new(2));
1946
1947 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1948 match unpacked {
1949 Cell::Padding(p) => {
1950 assert_eq!(payload, p.payload);
1951 }
1952 _ => panic!("Expected PaddingCell"),
1953 }
1954 }
1955
1956 #[test]
1957 fn test_padding_cell_wrong_size() {
1958 let result = PaddingCell::with_payload(vec![0x42u8; 100]);
1959 assert!(result.is_err());
1960 }
1961
1962 #[test]
1963 fn test_cell_unpack_all() {
1964 let versions = VersionsCell::new(vec![3, 4, 5]);
1965 let vpadding = VPaddingCell::with_payload(vec![0x08, 0x11]);
1966
1967 let mut combined = versions.pack(&LinkProtocol::new(2));
1968 combined.extend(vpadding.pack(&LinkProtocol::new(2)));
1969
1970 let cells = Cell::unpack_all(&combined, 2).unwrap();
1971 assert_eq!(2, cells.len());
1972
1973 match &cells[0] {
1974 Cell::Versions(v) => assert_eq!(vec![3, 4, 5], v.versions),
1975 _ => panic!("Expected VersionsCell"),
1976 }
1977
1978 match &cells[1] {
1979 Cell::VPadding(v) => assert_eq!(vec![0x08, 0x11], v.payload),
1980 _ => panic!("Expected VPaddingCell"),
1981 }
1982 }
1983
1984 #[test]
1985 fn test_destroy_cell_reasons() {
1986 let reasons = [
1987 (CloseReason::None, 0),
1988 (CloseReason::Protocol, 1),
1989 (CloseReason::Requested, 3),
1990 (CloseReason::Finished, 9),
1991 ];
1992
1993 for (reason, reason_int) in reasons {
1994 let cell = DestroyCell::new(1, reason);
1995 let packed = cell.pack(&LinkProtocol::new(5));
1996
1997 let (unpacked, _) = Cell::pop(&packed, 5).unwrap();
1998 match unpacked {
1999 Cell::Destroy(d) => {
2000 assert_eq!(1, d.circ_id);
2001 assert_eq!(reason, d.reason);
2002 assert_eq!(reason_int, d.reason_int);
2003 }
2004 _ => panic!("Expected DestroyCell"),
2005 }
2006 }
2007 }
2008}