Skip to main content

libtw2_net/
protocol.rs

1use arrayvec::ArrayVec;
2use libtw2_buffer as buffer;
3use libtw2_buffer::with_buffer;
4use libtw2_buffer::Buffer;
5use libtw2_buffer::BufferRef;
6use libtw2_common::boilerplate_packed;
7use libtw2_common::bytes::FromBytesExt as _;
8use libtw2_common::num::Cast;
9use libtw2_common::pretty;
10use libtw2_common::unwrap_or_return;
11use libtw2_huffman::instances::TEEWORLDS as HUFFMAN;
12use libtw2_warn::Ignore;
13use libtw2_warn::Warn;
14use std::cmp;
15use std::fmt;
16use std::io::Write as _;
17use std::str;
18use zerocopy::AsBytes as _;
19use zerocopy_derive::AsBytes;
20use zerocopy_derive::FromBytes;
21use zerocopy_derive::FromZeroes;
22
23pub const CHUNK_HEADER_SIZE: usize = 2;
24pub const CHUNK_HEADER_SIZE_VITAL: usize = 3;
25pub const HEADER_SIZE: usize = 3;
26pub const MAX_PACKETSIZE: usize = 1400;
27pub const PADDING_SIZE_CONNLESS: usize = 3;
28pub const TOKEN_SIZE: usize = 4;
29
30// For connectionless packets, this is obvious (MAX_PACKETSIZE - HEADER_SIZE -
31// PADDING_SIZE_CONNLESS). For packets sent in a connection context, you also
32// get a chunk header which replaces the connless padding (it's also 3 bytes
33// long), but here, we get a 4-byte token as well.
34pub const MAX_PAYLOAD: usize = 1390;
35
36pub const PACKETFLAG_CONTROL: u8 = 1 << 0;
37pub const PACKETFLAG_CONNLESS: u8 = 1 << 1;
38pub const PACKETFLAG_REQUEST_RESEND: u8 = 1 << 2;
39pub const PACKETFLAG_COMPRESSION: u8 = 1 << 3;
40
41pub const CHUNKFLAG_RESEND: u8 = 1 << 1;
42pub const CHUNKFLAG_VITAL: u8 = 1 << 0;
43
44pub const CTRLMSG_KEEPALIVE: u8 = 0;
45pub const CTRLMSG_CONNECT: u8 = 1;
46pub const CTRLMSG_CONNECTACCEPT: u8 = 2;
47pub const CTRLMSG_ACCEPT: u8 = 3;
48pub const CTRLMSG_CLOSE: u8 = 4;
49
50pub const TOKEN_NONE: Token = Token([0xff, 0xff, 0xff, 0xff]);
51pub const TOKEN_RESERVED: Token = Token([0x00, 0x00, 0x00, 0x00]);
52
53pub const CTRLMSG_CLOSE_REASON_LENGTH: usize = 127;
54pub const CTRLMSG_TOKEN_MAGIC: &[u8; 4] = b"TKEN";
55pub const CHUNK_FLAGS_BITS: u32 = 2;
56pub const CHUNK_SIZE_BITS: u32 = 10;
57pub const PACKET_FLAGS_BITS: u32 = 4;
58pub const SEQUENCE_BITS: u32 = 10;
59pub const SEQUENCE_MODULUS: u16 = 1 << SEQUENCE_BITS;
60
61pub fn chunk_header_size(vital: bool) -> usize {
62    if vital {
63        CHUNK_HEADER_SIZE_VITAL
64    } else {
65        CHUNK_HEADER_SIZE
66    }
67}
68
69#[derive(Debug)]
70pub enum Error {
71    Capacity(buffer::CapacityError),
72    TooLongData,
73}
74
75impl From<buffer::CapacityError> for Error {
76    fn from(e: buffer::CapacityError) -> Error {
77        Error::Capacity(e)
78    }
79}
80
81#[derive(Debug, Eq, PartialEq)]
82pub enum Warning {
83    ChunkHeaderPadding,
84    ChunkHeaderSequence,
85    ChunksNoChunks,
86    ChunksNumChunks,
87    ChunksUnknownData,
88    ConnlessPadding,
89    ControlConnectMissingTokenMagic,
90    ControlExcessData,
91    ControlFlags,
92    ControlNulTermination,
93    ControlNumChunks,
94    PacketHeaderPadding,
95}
96
97#[derive(Debug, Eq, PartialEq)]
98pub enum PacketReadError {
99    Compression,
100    ControlMissing,
101    ShortConnless,
102    TokenMissing,
103    TooLong,
104    TooShort,
105    UnknownControl,
106}
107
108#[derive(Clone, Copy)]
109pub enum ControlPacket<'a> {
110    KeepAlive,
111    Connect,
112    ConnectAccept,
113    Accept,
114    Close(&'a [u8]),
115}
116
117impl<'a> fmt::Debug for ControlPacket<'a> {
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        match *self {
120            ControlPacket::KeepAlive => f.debug_tuple("KeepAlive").finish(),
121            ControlPacket::Connect => f.debug_tuple("Connect").finish(),
122            ControlPacket::ConnectAccept => f.debug_tuple("ConnectAccept").finish(),
123            ControlPacket::Accept => f.debug_tuple("Accept").finish(),
124            ControlPacket::Close(reason) => f
125                .debug_tuple("Close")
126                .field(&pretty::AlmostString::new(reason))
127                .finish(),
128        }
129    }
130}
131
132#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
133pub struct Token(pub [u8; TOKEN_SIZE]);
134
135impl fmt::Debug for Token {
136    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137        write!(f, "{:08x}", u32::from_be_bytes(self.0))
138    }
139}
140
141impl fmt::Display for Token {
142    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143        fmt::Debug::fmt(self, f)
144    }
145}
146
147impl Token {
148    pub fn random<F: FnMut(&mut [u8])>(mut f: F) -> Token {
149        loop {
150            let mut token = TOKEN_NONE;
151            f(&mut token.0);
152            if token != TOKEN_NONE && token != TOKEN_RESERVED {
153                return token;
154            }
155        }
156    }
157}
158
159#[derive(Clone, Copy, Debug)]
160pub struct ConnectedPacket<'a> {
161    pub ack: u16, // u10
162    pub token: Option<Token>,
163    pub type_: ConnectedPacketType<'a>,
164}
165
166#[derive(Clone, Copy, Debug)]
167pub enum ConnectedPacketType<'a> {
168    // Chunks(request_resend, num_chunks, payload)
169    Chunks(bool, u8, &'a [u8]),
170    Control(ControlPacket<'a>),
171}
172
173#[derive(Clone, Copy, Debug)]
174pub enum Packet<'a> {
175    Connless(&'a [u8]),
176    Connected(ConnectedPacket<'a>),
177}
178
179#[derive(Clone, Copy, Debug)]
180pub struct Chunk<'a> {
181    pub data: &'a [u8],
182    // vital: Some((sequence, resend))
183    pub vital: Option<(u16, bool)>,
184}
185
186#[derive(Clone, Debug)]
187pub struct ChunksIter<'a> {
188    data: &'a [u8],
189    initial_len: usize,
190    num_remaining_chunks: i32,
191    checked_num_chunks_warning: bool,
192}
193
194impl<'a> ChunksIter<'a> {
195    pub fn new(data: &'a [u8], num_chunks: u8) -> ChunksIter<'a> {
196        ChunksIter {
197            data: data,
198            initial_len: data.len(),
199            num_remaining_chunks: num_chunks.i32(),
200            checked_num_chunks_warning: false,
201        }
202    }
203    fn excess_data<W: Warn<Warning>>(&mut self, warn: &mut W) -> Option<Chunk<'static>> {
204        warn.warn(Warning::ChunksUnknownData);
205        self.data = &[];
206        None
207    }
208    pub fn pos(&self) -> usize {
209        self.initial_len - self.data.len()
210    }
211    pub fn next_warn<W>(&mut self, warn: &mut W) -> Option<Chunk<'a>>
212    where
213        W: Warn<Warning>,
214    {
215        if self.data.len() == 0 {
216            if !self.checked_num_chunks_warning {
217                self.checked_num_chunks_warning = true;
218                if self.num_remaining_chunks != 0 {
219                    warn.warn(Warning::ChunksNumChunks);
220                }
221            }
222            return None;
223        }
224        let (header, sequence, chunk_data_and_rest) =
225            unwrap_or_return!(read_chunk_header(warn, self.data), self.excess_data(warn));
226        let vital = sequence.map(|s| (s, header.flags & CHUNKFLAG_RESEND != 0));
227        let size = header.size.usize();
228        if chunk_data_and_rest.len() < size {
229            return self.excess_data(warn);
230        }
231        let (chunk_data, rest) = chunk_data_and_rest.split_at(size);
232        self.data = rest;
233        self.num_remaining_chunks -= 1;
234        Some(Chunk {
235            data: chunk_data,
236            vital: vital,
237        })
238    }
239}
240
241impl<'a> Iterator for ChunksIter<'a> {
242    type Item = Chunk<'a>;
243    fn next(&mut self) -> Option<Chunk<'a>> {
244        self.next_warn(&mut Ignore)
245    }
246    fn size_hint(&self) -> (usize, Option<usize>) {
247        let len = self.clone().count();
248        (len, Some(len))
249    }
250}
251
252impl<'a> ExactSizeIterator for ChunksIter<'a> {}
253
254// TODO: Make this a member function of `Chunk`
255// vital: Some((sequence, resend))
256pub fn write_chunk<'a, B: Buffer<'a>>(
257    bytes: &[u8],
258    vital: Option<(u16, bool)>,
259    buffer: B,
260) -> Result<&'a [u8], buffer::CapacityError> {
261    with_buffer(buffer, |b| write_chunk_impl(bytes, vital, b))
262}
263
264pub fn write_chunk_impl<'d, 's>(
265    bytes: &[u8],
266    vital: Option<(u16, bool)>,
267    mut buffer: BufferRef<'d, 's>,
268) -> Result<&'d [u8], buffer::CapacityError> {
269    assert!(bytes.len() >> CHUNK_SIZE_BITS == 0);
270    let size = bytes.len().assert_u16();
271
272    let (sequence, resend) = vital.unwrap_or((0, false));
273    let resend_flag = if resend { CHUNKFLAG_RESEND } else { 0 };
274    let vital_flag = if vital.is_some() { CHUNKFLAG_VITAL } else { 0 };
275    let flags = vital_flag | resend_flag;
276
277    let header_nonvital = ChunkHeader {
278        flags: flags,
279        size: size,
280    };
281
282    let header1;
283    let header2;
284    let header: &[u8] = if vital.is_some() {
285        header1 = ChunkHeaderVital {
286            h: header_nonvital,
287            sequence: sequence,
288        }
289        .pack();
290        header1.as_bytes()
291    } else {
292        header2 = header_nonvital.pack();
293        header2.as_bytes()
294    };
295    buffer.write(header)?;
296    buffer.write(bytes)?;
297    Ok(buffer.initialized())
298}
299
300fn write_connless_packet<'a, B: Buffer<'a>>(payload: &[u8], buffer: B) -> Result<&'a [u8], Error> {
301    fn inner<'d, 's>(payload: &[u8], mut buffer: BufferRef<'d, 's>) -> Result<&'d [u8], Error> {
302        if payload.len() > MAX_PAYLOAD {
303            return Err(Error::TooLongData);
304        }
305        buffer.write(&[b'\xff'; HEADER_SIZE + PADDING_SIZE_CONNLESS])?;
306        buffer.write(payload)?;
307        Ok(buffer.initialized())
308    }
309
310    with_buffer(buffer, |b| inner(payload, b))
311}
312
313fn has_token_heuristic(control: bool, num_chunks: u8, payload: &[u8]) -> bool {
314    let payload_end_heuristic = if control {
315        let (&control, payload) = unwrap_or_return!(payload.split_first(), false);
316        match control {
317            CTRLMSG_CONNECT | CTRLMSG_CONNECTACCEPT => {
318                if payload.len() < 4 || &payload[0..4] != CTRLMSG_TOKEN_MAGIC {
319                    return false;
320                }
321                1 + 4
322            }
323            CTRLMSG_CLOSE => {
324                let nul = payload
325                    .iter()
326                    .position(|&b| b == 0)
327                    .unwrap_or(payload.len());
328                // 4-byte payloads are ambiguous. It might either be a 3-byte
329                // reason with nul byte or a 0-byte reason with a 4-byte token.
330                //
331                // Heuristically, we check whether it can realistically be a
332                // 3-byte reason with nul byte, by checking whether it decodes
333                // as UTF-8. This gives a probability 2650112 / 256**4 ≈
334                // 0.0006 of a false-positive.
335                //
336                // ```
337                // >>> def d(s):
338                // ...     try:
339                // ...         s.decode("utf-8")
340                // ...         return True
341                // ...     except UnicodeDecodeError:
342                // ...         return False
343                // ...
344                // >>> sum(d(i.to_bytes(3, 'big')) for i in range(256**3))
345                // 2650112
346                // ```
347                if payload.len() == 4 && (nul != 3 || str::from_utf8(&payload[0..3]).is_err()) {
348                    return true;
349                }
350                1 + nul + 1
351            }
352            _ => 1,
353        }
354    } else {
355        let mut chunks_iter = ChunksIter::new(payload, num_chunks);
356        for _ in 0..num_chunks {
357            if chunks_iter.next_warn(&mut Ignore).is_none() {
358                return false;
359            }
360        }
361        chunks_iter.pos()
362    };
363    payload_end_heuristic + TOKEN_SIZE <= payload.len()
364}
365
366impl<'a> Packet<'a> {
367    pub fn is_initial(packet: &[u8]) -> bool {
368        if packet.len() > MAX_PACKETSIZE {
369            return false;
370        }
371        let (header, payload) =
372            unwrap_or_return!(PacketHeaderPacked::ref_and_rest_from(packet), false);
373        let header = header.unpack_warn(&mut Ignore);
374        if header.flags & PACKETFLAG_CONNLESS != 0 {
375            return true;
376        }
377        let ctrl = payload.first().copied();
378        header.flags & !PACKETFLAG_REQUEST_RESEND == PACKETFLAG_CONTROL
379            && (ctrl == Some(CTRLMSG_CONNECT) || ctrl == Some(CTRLMSG_ACCEPT))
380    }
381    fn needs_decompression(packet: &[u8]) -> bool {
382        if packet.len() > MAX_PACKETSIZE {
383            return false;
384        }
385        let (header, _) = unwrap_or_return!(PacketHeaderPacked::ref_and_rest_from(packet), false);
386        let header = header.unpack_warn(&mut Ignore);
387        header.flags & PACKETFLAG_CONNLESS == 0 && header.flags & PACKETFLAG_COMPRESSION != 0
388    }
389    /// Parse a packet.
390    ///
391    /// `token_hint` tells the decoder whether to expect a DDNet 0.6-style
392    /// token. If you call this as part of an existing connection, pass
393    /// `Some(bool)` to tell whether this connection uses the token. If you
394    /// call this on a packet not associated to a connection, use `None`.
395    ///
396    /// `buffer` needs to have at least size `MAX_PAYLOAD`.
397    pub fn read<'b, B, W>(
398        warn: &mut W,
399        bytes: &'b [u8],
400        token_hint: Option<bool>,
401        buffer: B,
402    ) -> Result<Packet<'b>, PacketReadError>
403    where
404        B: Buffer<'b>,
405        W: Warn<Warning>,
406    {
407        with_buffer(buffer, |b| {
408            Packet::read_impl(warn, bytes, token_hint, Some(b))
409        })
410    }
411    pub fn read_panic_on_decompression<'b, W>(
412        warn: &mut W,
413        bytes: &'b [u8],
414        token_hint: Option<bool>,
415    ) -> Result<Packet<'b>, PacketReadError>
416    where
417        W: Warn<Warning>,
418    {
419        Packet::read_impl(warn, bytes, token_hint, None)
420    }
421    fn read_impl<'d, 's, W>(
422        warn: &mut W,
423        bytes: &'d [u8],
424        token_hint: Option<bool>,
425        buffer: Option<BufferRef<'d, 's>>,
426    ) -> Result<Packet<'d>, PacketReadError>
427    where
428        W: Warn<Warning>,
429    {
430        use self::PacketReadError::*;
431
432        assert!(buffer
433            .as_ref()
434            .map(|b| b.remaining() >= MAX_PACKETSIZE)
435            .unwrap_or(true));
436        if bytes.len() > MAX_PACKETSIZE {
437            return Err(TooLong);
438        }
439        let (header, payload) =
440            unwrap_or_return!(PacketHeaderPacked::ref_and_rest_from(bytes), Err(TooShort));
441        let header = header.unpack_warn(warn);
442        if header.flags & PACKETFLAG_CONNLESS != 0 {
443            if payload.len() < PADDING_SIZE_CONNLESS {
444                return Err(ShortConnless);
445            }
446            let (padding, payload) = payload.split_at(PADDING_SIZE_CONNLESS);
447            if !padding.iter().all(|&b| b == 0xff) || !bytes[..3].iter().all(|&b| b == 0xff) {
448                warn.warn(Warning::ConnlessPadding);
449            }
450            return Ok(Packet::Connless(payload));
451        }
452
453        let payload = if header.flags & PACKETFLAG_COMPRESSION != 0 {
454            let mut buffer =
455                buffer.expect("read_panic_on_decompression called on compressed packet");
456            let decompressed = Packet::decompress(bytes, &mut buffer).map_err(|_| Compression)?;
457            let (_, payload) = PacketHeaderPacked::ref_and_rest_from(decompressed).unwrap();
458            payload
459        } else {
460            payload
461        };
462
463        if payload.len() > MAX_PACKETSIZE - HEADER_SIZE {
464            return Err(Compression);
465        }
466
467        let ack = header.ack;
468
469        let has_token = token_hint.unwrap_or_else(|| {
470            let control = header.flags & PACKETFLAG_CONTROL != 0;
471            has_token_heuristic(control, header.num_chunks, payload)
472        });
473
474        let (payload, token) = if has_token {
475            if payload.len() < TOKEN_SIZE {
476                return Err(TokenMissing);
477            }
478            let (payload, t_bytes) = payload.split_at(payload.len() - TOKEN_SIZE);
479            let token = Token([t_bytes[0], t_bytes[1], t_bytes[2], t_bytes[3]]);
480            (payload, Some(token))
481        } else {
482            (payload, None)
483        };
484
485        let type_ = if header.flags & PACKETFLAG_CONTROL != 0 {
486            if header.num_chunks != 0 {
487                warn.warn(Warning::ControlNumChunks);
488            }
489            if header.flags & PACKETFLAG_COMPRESSION != 0
490                || header.flags & PACKETFLAG_REQUEST_RESEND != 0
491            {
492                // TODO: Should we handle these flags? Vanilla does that too.
493                warn.warn(Warning::ControlFlags);
494            }
495
496            let (&control, payload) = unwrap_or_return!(payload.split_first(), Err(ControlMissing));
497            // check for excess data
498            match control {
499                CTRLMSG_CONNECT | CTRLMSG_CONNECTACCEPT => {
500                    if token.is_some() {
501                        if !payload.starts_with(CTRLMSG_TOKEN_MAGIC) {
502                            warn.warn(Warning::ControlConnectMissingTokenMagic);
503                            if !payload.is_empty() {
504                                warn.warn(Warning::ControlExcessData);
505                            }
506                        } else {
507                            if payload.len() > CTRLMSG_TOKEN_MAGIC.len() {
508                                warn.warn(Warning::ControlExcessData);
509                            }
510                        }
511                    } else {
512                        if !payload.is_empty() {
513                            warn.warn(Warning::ControlExcessData);
514                        }
515                    }
516                }
517                CTRLMSG_CLOSE => {} // handled later
518                _ => {
519                    if payload.len() != 0 {
520                        warn.warn(Warning::ControlExcessData);
521                    }
522                }
523            }
524            let control = match control {
525                CTRLMSG_KEEPALIVE => ControlPacket::KeepAlive,
526                CTRLMSG_CONNECT => ControlPacket::Connect,
527                CTRLMSG_CONNECTACCEPT => ControlPacket::ConnectAccept,
528                CTRLMSG_ACCEPT => ControlPacket::Accept,
529                CTRLMSG_CLOSE => {
530                    let nul = payload
531                        .iter()
532                        .position(|&b| b == 0)
533                        .unwrap_or(payload.len());
534                    let nul = cmp::min(nul, CTRLMSG_CLOSE_REASON_LENGTH);
535                    if payload.len() != 0 && nul + 1 != payload.len() {
536                        if nul + 1 < payload.len() {
537                            warn.warn(Warning::ControlExcessData);
538                        } else {
539                            warn.warn(Warning::ControlNulTermination);
540                        }
541                    }
542                    ControlPacket::Close(&payload[..nul])
543                }
544                _ => {
545                    // Unrecognized control packet.
546                    return Err(UnknownControl);
547                }
548            };
549
550            ConnectedPacketType::Control(control)
551        } else {
552            let request_resend = header.flags & PACKETFLAG_REQUEST_RESEND != 0;
553            if header.num_chunks == 0 && !request_resend {
554                warn.warn(Warning::ChunksNoChunks);
555            }
556            ConnectedPacketType::Chunks(request_resend, header.num_chunks, payload)
557        };
558
559        Ok(Packet::Connected(ConnectedPacket {
560            token: token,
561            ack: ack,
562            type_: type_,
563        }))
564    }
565    /// `buffer` needs to have at least size `MAX_PACKETSIZE`.
566    pub fn decompress_if_needed<B: Buffer<'a>>(
567        packet: &[u8],
568        buffer: B,
569    ) -> Result<bool, libtw2_huffman::DecompressionError> {
570        with_buffer(buffer, |b| Packet::decompress_if_needed_impl(packet, b))
571    }
572    fn decompress_if_needed_impl<'d, 's>(
573        packet: &[u8],
574        mut buffer: BufferRef<'d, 's>,
575    ) -> Result<bool, libtw2_huffman::DecompressionError> {
576        assert!(buffer.remaining() >= MAX_PACKETSIZE);
577        if !Packet::needs_decompression(packet) {
578            return Ok(false);
579        }
580        Packet::decompress(packet, &mut buffer)?;
581        Ok(true)
582    }
583
584    fn decompress<B: Buffer<'a>>(
585        packet: &[u8],
586        buffer: B,
587    ) -> Result<&'a [u8], libtw2_huffman::DecompressionError> {
588        with_buffer(buffer, |b| Packet::decompress_impl(packet, b))
589    }
590    fn decompress_impl<'d, 's>(
591        packet: &[u8],
592        mut buffer: BufferRef<'d, 's>,
593    ) -> Result<&'d [u8], libtw2_huffman::DecompressionError> {
594        assert!(buffer.remaining() >= MAX_PACKETSIZE);
595        assert!(Packet::needs_decompression(packet));
596        let (header, payload) = PacketHeaderPacked::ref_and_rest_from(packet)
597            .expect("packet passed to decompress too short for header");
598        let header = header.unpack_warn(&mut Ignore);
599        assert!(header.flags & PACKETFLAG_CONNLESS == 0);
600        assert!(header.flags & PACKETFLAG_COMPRESSION != 0);
601
602        let fake_header = PacketHeader {
603            flags: header.flags & !PACKETFLAG_COMPRESSION,
604            ack: header.ack,
605            num_chunks: header.num_chunks,
606        };
607        buffer.write(fake_header.pack().as_bytes()).unwrap();
608        HUFFMAN.decompress(payload, &mut buffer)?;
609
610        Ok(buffer.initialized())
611    }
612
613    pub fn write<'b, B: Buffer<'b>>(&self, buffer: B) -> Result<&'b [u8], Error> {
614        match *self {
615            Packet::Connected(ref p) => with_buffer(buffer, |b| p.write_impl(b)),
616            Packet::Connless(ref d) => write_connless_packet(d, buffer),
617        }
618    }
619}
620
621impl<'a> ConnectedPacket<'a> {
622    pub fn write<'b, B: Buffer<'b>>(&self, buffer: B) -> Result<&'b [u8], Error> {
623        with_buffer(buffer, |b| self.write_impl(b))
624    }
625
626    fn write_impl<'d, 's>(&self, mut buffer: BufferRef<'d, 's>) -> Result<&'d [u8], Error> {
627        match self.type_ {
628            ConnectedPacketType::Chunks(request_resend, num_chunks, payload) => {
629                let mut token_buffer: ArrayVec<[u8; 2048]> = ArrayVec::new();
630                let payload: &[u8] = if let Some(token) = self.token {
631                    token_buffer.write(payload).unwrap();
632                    token_buffer.write(&token.0).unwrap();
633                    &token_buffer
634                } else {
635                    payload
636                };
637                let mut compression_buffer: ArrayVec<[u8; 2048]> = ArrayVec::new();
638                let mut compression = 0;
639                let comp_result = HUFFMAN.compress(payload, &mut compression_buffer);
640                if comp_result
641                    .map(|s| s.len() < payload.len())
642                    .unwrap_or(false)
643                {
644                    compression = PACKETFLAG_COMPRESSION;
645                }
646                let request_resend = if request_resend {
647                    PACKETFLAG_REQUEST_RESEND
648                } else {
649                    0
650                };
651                buffer.write(
652                    PacketHeader {
653                        flags: request_resend | compression,
654                        ack: self.ack,
655                        num_chunks: num_chunks,
656                    }
657                    .pack()
658                    .as_bytes(),
659                )?;
660                buffer.write(if compression != 0 {
661                    &compression_buffer
662                } else {
663                    payload
664                })?;
665                Ok(buffer.initialized())
666            }
667            ConnectedPacketType::Control(c) => c.write(self.token, self.ack, buffer),
668        }
669    }
670}
671
672impl<'a> ControlPacket<'a> {
673    fn write<'d, 's>(
674        &self,
675        token: Option<Token>,
676        ack: u16,
677        mut buffer: BufferRef<'d, 's>,
678    ) -> Result<&'d [u8], Error> {
679        buffer.write(
680            PacketHeader {
681                flags: PACKETFLAG_CONTROL,
682                ack: ack,
683                num_chunks: 0,
684            }
685            .pack()
686            .as_bytes(),
687        )?;
688        let magic = match *self {
689            ControlPacket::KeepAlive => CTRLMSG_KEEPALIVE,
690            ControlPacket::Connect => CTRLMSG_CONNECT,
691            ControlPacket::ConnectAccept => CTRLMSG_CONNECTACCEPT,
692            ControlPacket::Accept => CTRLMSG_ACCEPT,
693            ControlPacket::Close(..) => CTRLMSG_CLOSE,
694        };
695        buffer.write(&[magic])?;
696        if matches!(*self, ControlPacket::Connect | ControlPacket::ConnectAccept) && token.is_some()
697        {
698            buffer.write(CTRLMSG_TOKEN_MAGIC)?;
699        }
700        match *self {
701            ControlPacket::Close(m) => {
702                assert!(m.iter().all(|&b| b != 0));
703                buffer.write(m)?;
704                buffer.write(&[0])?;
705            }
706            _ => {}
707        }
708        if let Some(token) = token {
709            buffer.write(&token.0)?;
710        }
711        let result = buffer.initialized();
712        assert!(result.len() <= MAX_PACKETSIZE);
713        Ok(result)
714    }
715}
716
717#[repr(C, packed)]
718#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)]
719pub struct PacketHeaderPacked {
720    flags_padding_ack: u8, // u4 u2 u2
721    ack: u8,
722    num_chunks: u8,
723}
724
725#[derive(Copy, Clone, Debug, Eq, PartialEq)]
726pub struct PacketHeader {
727    pub flags: u8, // u4
728    pub ack: u16,  // u10
729    pub num_chunks: u8,
730}
731
732impl PacketHeaderPacked {
733    pub fn unpack_warn<W: Warn<Warning>>(self, warn: &mut W) -> PacketHeader {
734        let PacketHeaderPacked {
735            flags_padding_ack,
736            ack,
737            num_chunks,
738        } = self;
739        // First clause checks whether PACKETFLAG_CONNLESS is set.
740        if flags_padding_ack & 0b0010_0000 == 0 && flags_padding_ack & 0b0000_1100 != 0 {
741            warn.warn(Warning::PacketHeaderPadding);
742        }
743        PacketHeader {
744            flags: (flags_padding_ack & 0b1111_0000) >> 4,
745            ack: (((flags_padding_ack & 0b0000_0011) as u16) << 8) | (ack as u16),
746            num_chunks: num_chunks,
747        }
748    }
749    pub fn unpack(self) -> PacketHeader {
750        self.unpack_warn(&mut Ignore)
751    }
752}
753
754impl PacketHeader {
755    pub fn pack(self) -> PacketHeaderPacked {
756        let PacketHeader {
757            flags,
758            ack,
759            num_chunks,
760        } = self;
761        // Check that the fields do not exceed their maximal size.
762        assert!(flags >> PACKET_FLAGS_BITS == 0);
763        assert!(ack >> SEQUENCE_BITS == 0);
764        PacketHeaderPacked {
765            flags_padding_ack: flags << 4 | (ack >> 8) as u8,
766            ack: ack as u8,
767            num_chunks: num_chunks,
768        }
769    }
770}
771
772#[derive(Copy, Clone, Debug, Eq, PartialEq)]
773pub struct ChunkHeader {
774    pub flags: u8, // u2
775    pub size: u16, // u10
776}
777
778#[derive(Copy, Clone, Debug, Eq, PartialEq)]
779pub struct ChunkHeaderVital {
780    pub h: ChunkHeader,
781    pub sequence: u16, // u16
782}
783
784#[repr(C, packed)]
785#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)]
786pub struct ChunkHeaderPacked {
787    flags_size: u8,   // u2 u6
788    padding_size: u8, // u4 u4
789}
790
791#[repr(C, packed)]
792#[derive(AsBytes, Clone, Copy, FromBytes, FromZeroes)]
793pub struct ChunkHeaderVitalPacked {
794    flags_size: u8,    // u2 u6
795    sequence_size: u8, // u4 u4
796    sequence: u8,
797}
798
799/// -> Some((chunk_header, sequence, rest))
800pub fn read_chunk_header<'a, W>(
801    warn: &mut W,
802    data: &'a [u8],
803) -> Option<(ChunkHeader, Option<u16>, &'a [u8])>
804where
805    W: Warn<Warning>,
806{
807    let (raw_header, chunk_data_and_rest) =
808        unwrap_or_return!(ChunkHeaderPacked::ref_and_rest_from(data));
809
810    let header = raw_header.unpack_warn(&mut Ignore);
811    Some(if header.flags & CHUNKFLAG_VITAL != 0 {
812        let (header, chunk_data_and_rest_vital) =
813            unwrap_or_return!(ChunkHeaderVitalPacked::ref_and_rest_from(data));
814        let header = header.unpack_warn(warn);
815        (header.h, Some(header.sequence), chunk_data_and_rest_vital)
816    } else {
817        raw_header.unpack_warn(warn);
818        (header, None, chunk_data_and_rest)
819    })
820}
821
822impl ChunkHeaderPacked {
823    pub fn unpack_warn<W: Warn<Warning>>(self, warn: &mut W) -> ChunkHeader {
824        let ChunkHeaderPacked {
825            flags_size,
826            padding_size,
827        } = self;
828        if padding_size & 0b1111_0000 != 0 {
829            warn.warn(Warning::ChunkHeaderPadding);
830        }
831        ChunkHeader {
832            flags: (flags_size & 0b1100_0000) >> 6,
833            size: ((((flags_size & 0b0011_1111) as u16) << 4)
834                | (padding_size & 0b0000_1111) as u16),
835        }
836    }
837    pub fn unpack(self) -> ChunkHeader {
838        self.unpack_warn(&mut Ignore)
839    }
840}
841
842impl ChunkHeader {
843    pub fn pack(self) -> ChunkHeaderPacked {
844        let ChunkHeader { flags, size } = self;
845        // Check that the fields do not exceed their maximal size.
846        assert!(flags >> CHUNK_FLAGS_BITS == 0);
847        assert!(size >> CHUNK_SIZE_BITS == 0);
848        ChunkHeaderPacked {
849            flags_size: (flags & 0b11) << 6 | ((size & 0b11_1111_0000) >> 4) as u8,
850            padding_size: (size & 0b00_0000_1111) as u8,
851        }
852    }
853}
854
855impl ChunkHeaderVitalPacked {
856    pub fn unpack_warn<W: Warn<Warning>>(self, warn: &mut W) -> ChunkHeaderVital {
857        let ChunkHeaderVitalPacked {
858            flags_size,
859            sequence_size,
860            sequence,
861        } = self;
862        if (sequence_size & 0b0011_0000) >> 4 != (sequence & 0b1100_0000) >> 6 {
863            warn.warn(Warning::ChunkHeaderSequence);
864        }
865        ChunkHeaderVital {
866            h: ChunkHeaderPacked {
867                flags_size: flags_size,
868                padding_size: sequence_size & 0b0000_1111,
869            }
870            .unpack_warn(warn),
871            sequence: ((sequence_size & 0b1111_0000) as u16) << 2
872                | ((sequence & 0b1111_1111) as u16),
873        }
874    }
875    pub fn unpack(self) -> ChunkHeaderVital {
876        self.unpack_warn(&mut Ignore)
877    }
878}
879
880impl ChunkHeaderVital {
881    pub fn pack(self) -> ChunkHeaderVitalPacked {
882        let ChunkHeaderVital { h, sequence } = self;
883        assert!(sequence >> SEQUENCE_BITS == 0);
884        let ChunkHeaderPacked {
885            flags_size,
886            padding_size,
887        } = h.pack();
888        ChunkHeaderVitalPacked {
889            flags_size: flags_size,
890            sequence_size: (padding_size & 0b0000_1111) | ((sequence & 0b11_1100_0000) >> 2) as u8,
891            sequence: (sequence & 0b00_1111_1111) as u8,
892        }
893    }
894}
895
896boilerplate_packed!(PacketHeaderPacked, HEADER_SIZE, test_ph_size);
897boilerplate_packed!(ChunkHeaderPacked, CHUNK_HEADER_SIZE, test_ch_size);
898boilerplate_packed!(
899    ChunkHeaderVitalPacked,
900    CHUNK_HEADER_SIZE_VITAL,
901    test_chv_size
902);
903
904#[cfg(test)]
905mod test {
906    use super::ChunkHeader;
907    use super::ChunkHeaderPacked;
908    use super::ChunkHeaderVital;
909    use super::ChunkHeaderVitalPacked;
910    use super::ChunksIter;
911    use super::ConnectedPacket;
912    use super::ConnectedPacketType;
913    use super::Packet;
914    use super::PacketHeader;
915    use super::PacketHeaderPacked;
916    use super::PacketReadError;
917    use super::Warning;
918    use super::CHUNK_FLAGS_BITS;
919    use super::CHUNK_SIZE_BITS;
920    use super::MAX_PACKETSIZE;
921    use super::PACKET_FLAGS_BITS;
922    use super::SEQUENCE_BITS;
923    use libtw2_common::bytes::FromBytesExt;
924    use libtw2_warn::Ignore;
925    use libtw2_warn::Panic;
926    use libtw2_warn::Warn;
927    use quickcheck::quickcheck;
928    use zerocopy::AsBytes as _;
929
930    struct WarnVec<'a>(&'a mut Vec<Warning>);
931
932    impl<'a> Warn<Warning> for WarnVec<'a> {
933        fn warn(&mut self, warning: Warning) {
934            self.0.push(warning);
935        }
936    }
937
938    fn collect_warnings(
939        token_hint: Option<bool>,
940        input: &[u8],
941        strip_chunks_no_chunks: bool,
942    ) -> Vec<Warning> {
943        let mut result = vec![];
944        let mut buffer = Vec::with_capacity(4096);
945        let packet =
946            Packet::read(&mut WarnVec(&mut result), input, token_hint, &mut buffer).unwrap();
947        if let Packet::Connected(ConnectedPacket {
948            type_: ConnectedPacketType::Chunks(_, num_chunks, chunk_data),
949            ..
950        }) = packet
951        {
952            let mut chunks = ChunksIter::new(chunk_data, num_chunks);
953            while let Some(_) = chunks.next_warn(&mut WarnVec(&mut result)) {}
954        }
955        if strip_chunks_no_chunks {
956            result.retain(|w| *w != Warning::ChunksNoChunks);
957        }
958        result
959    }
960
961    fn assert_warnings(has_token: bool, skip_heur: bool, input: &[u8], warnings: &[Warning]) {
962        let strip = warnings != &[Warning::ChunksNoChunks];
963        assert_eq!(collect_warnings(Some(has_token), input, strip), warnings);
964        if !skip_heur {
965            assert_eq!(collect_warnings(None, input, strip), warnings);
966        }
967    }
968
969    pub fn assert_warn(has_token: bool, skip_heur: bool, input: &[u8], warning: Warning) {
970        assert_warnings(has_token, skip_heur, input, &[warning]);
971    }
972
973    pub fn assert_no_warn(has_token: bool, skip_heur: bool, input: &[u8]) {
974        assert_warnings(has_token, skip_heur, input, &[]);
975    }
976
977    pub fn assert_err(has_token: bool, skip_heur: bool, input: &[u8], error: PacketReadError) {
978        let mut buffer = Vec::with_capacity(4096);
979        assert_eq!(
980            Packet::read(&mut Panic, input, Some(has_token), &mut buffer).unwrap_err(),
981            error
982        );
983        if !skip_heur {
984            assert_eq!(
985                Packet::read(&mut Panic, input, None, &mut buffer).unwrap_err(),
986                error
987            );
988        }
989    }
990
991    quickcheck! {
992        fn packet_header_roundtrip(flags: u8, ack: u16, num_chunks: u8) -> bool {
993            let flags = flags ^ (flags >> PACKET_FLAGS_BITS << PACKET_FLAGS_BITS);
994            let ack = ack ^ (ack >> SEQUENCE_BITS << SEQUENCE_BITS);
995            let packet_header = PacketHeader {
996                flags: flags,
997                ack: ack,
998                num_chunks: num_chunks,
999            };
1000            packet_header == packet_header.pack().unpack()
1001        }
1002
1003        fn packet_header_unpack(v: (u8, u8, u8)) -> bool {
1004            // Two bits must be zeroed (see doc/packet.md).
1005            let v0 = v.0 & 0b1111_0011;
1006            let bytes = &[v0, v.1, v.2];
1007            PacketHeaderPacked::ref_from_array(bytes).unpack().pack().as_bytes() == bytes
1008        }
1009
1010        fn chunk_header_roundtrip(flags: u8, size: u16, sequence: u16) -> bool {
1011            let flags = flags ^ (flags >> CHUNK_FLAGS_BITS << CHUNK_FLAGS_BITS);
1012            let size = size ^ (size >> CHUNK_SIZE_BITS << CHUNK_SIZE_BITS);
1013            let sequence = sequence ^ (sequence >> SEQUENCE_BITS << SEQUENCE_BITS);
1014            let chunk_header = ChunkHeader {
1015                flags: flags,
1016                size: size,
1017            };
1018            let chunk_header_vital = ChunkHeaderVital {
1019                h: chunk_header,
1020                sequence: sequence,
1021            };
1022            chunk_header == chunk_header.pack().unpack()
1023                && chunk_header_vital == chunk_header_vital.pack().unpack()
1024        }
1025
1026        fn chunk_header_unpack(v: (u8, u8, u8)) -> bool {
1027            let bytes2 = &[v.0, v.1];
1028            let bytes3 = &[v.0, v.1, v.2];
1029            let bytes2_result = &[v.0, v.1 & 0b0000_1111];
1030            let bytes3_result = &[v.0, v.1 | ((v.2 & 0b1100_0000) >> 2), v.2 | ((v.1 & 0b0011_0000) << 2)];
1031            ChunkHeaderPacked::ref_from_array(bytes2).unpack().pack().as_bytes() == bytes2_result
1032                && ChunkHeaderVitalPacked::ref_from_array(bytes3).unpack().pack().as_bytes() == bytes3_result
1033        }
1034
1035        fn packet_read_no_panic(data: Vec<u8>) -> bool {
1036            let mut buffer = [0; MAX_PACKETSIZE];
1037            let _ = Packet::read(&mut Ignore, &data, None, &mut buffer[..]);
1038            true
1039        }
1040    }
1041}
1042
1043#[cfg(test)]
1044#[rustfmt::skip]
1045mod test_no_token {
1046    use libtw2_warn::Ignore;
1047    use quickcheck::quickcheck;
1048    use super::MAX_PACKETSIZE;
1049    use super::Packet;
1050    use super::PacketReadError::*;
1051    use super::PacketReadError;
1052    use super::Warning::*;
1053    use super::Warning;
1054
1055    fn assert_warn(input: &[u8], warning: Warning) {
1056        super::test::assert_warn(false, false, input, warning);
1057    }
1058
1059    fn assert_no_warn(input: &[u8]) {
1060        super::test::assert_no_warn(false, false, input);
1061    }
1062
1063    fn assert_err(input: &[u8], error: PacketReadError) {
1064        super::test::assert_err(false, false, input, error);
1065    }
1066
1067    #[test] fn w_chp() { assert_warn(b"\x00\x00\x01\x00\xf0", ChunkHeaderPadding) }
1068    #[test] fn w_chs1() { assert_warn(b"\x00\x00\x01\x40\x20\x00", ChunkHeaderSequence) }
1069    #[test] fn w_chs2() { assert_warn(b"\x00\x00\x01\x40\x10\x00", ChunkHeaderSequence) }
1070    #[test] fn w_chs3() { assert_no_warn(b"\x00\x00\x01\x40\x70\xcf") }
1071    #[test] fn w_cud1() { assert_warn(b"\x00\x00\x00\xff", ChunksUnknownData) }
1072    #[test] fn w_cud2() { assert_warn(b"\x00\x00\x01\x00\x00\x00", ChunksUnknownData) }
1073    #[test] fn w_cud3() { assert_no_warn(b"\x00\x00\x01\x00\x00") }
1074    #[test] fn w_cnc1() { assert_warn(b"\x00\x00\x01", ChunksNumChunks) }
1075    #[test] fn w_cnc2() { assert_warn(b"\x00\x00\x00\x00\x00", ChunksNumChunks) }
1076    #[test] fn w_cnc_() { assert_warn(b"\x00\x00\x00", ChunksNoChunks) }
1077    #[test] fn w_cp1() { assert_warn(b"xe\x01\x02\x03\x04", ConnlessPadding) }
1078    #[test] fn w_cp2() { assert_warn(b"\xff\xff\xff\xff\xff\xfe", ConnlessPadding) }
1079    #[test] fn w_cp3() { assert_warn(b"\x7f\xff\xff\xff\xff\xff", ConnlessPadding) }
1080    #[test] fn w_cp4() { assert_no_warn(b"\xff\xff\xff\xff\xff\xff") }
1081    #[test] fn w_ced1() { assert_warn(b"\x10\x00\x00\x00\x00", ControlExcessData) }
1082    #[test] fn w_ced2() { assert_warn(b"\x10\x00\x00\x04\x00\x00", ControlExcessData) }
1083    #[test] fn w_cf1() { assert_warn(b"\x90\x00\x00\x15\x37", ControlFlags) }
1084    #[test] fn w_cf2() { assert_warn(b"\x50\x00\x00\x00", ControlFlags) }
1085    #[test] fn w_cnt1() { assert_warn(b"\x10\x00\x00\x04\x01", ControlNulTermination) }
1086    #[test] fn w_cnt2() { assert_no_warn(b"\x10\x00\x00\x04") }
1087    #[test] fn w_cnc() { assert_warn(b"\x10\x00\xff\x00", ControlNumChunks) }
1088    #[test] fn w_php1() { assert_warn(b"\x08\x00\x00", PacketHeaderPadding) }
1089    #[test] fn w_php2() { assert_warn(b"\x04\x00\x00", PacketHeaderPadding) }
1090
1091    #[test] fn e_cm() { assert_err(b"\x10\x00\x00", ControlMissing) }
1092    #[test] fn e_sc() { assert_err(b"\xff\xff\xff", ShortConnless) }
1093    #[test] fn e_tl() { assert_err(&[0; MAX_PACKETSIZE+1], TooLong) }
1094    #[test] fn e_ts1() { assert_err(b"\x00\x00", TooShort) }
1095    #[test] fn e_ts2() { assert_err(b"", TooShort) }
1096    #[test] fn e_uc1() { assert_err(b"\x10\x00\x00\x05", UnknownControl) }
1097    #[test] fn e_uc2() { assert_err(b"\x10\x00\x00\xff", UnknownControl) }
1098    #[test] fn e_c() { assert_err(b"\x80\x00\x00", Compression) }
1099
1100    quickcheck! {
1101        fn packet_read_no_panic(data: Vec<u8>) -> bool {
1102            let mut buffer = [0; MAX_PACKETSIZE];
1103            let _ = Packet::read(&mut Ignore, &data, Some(false), &mut buffer[..]);
1104            true
1105        }
1106    }
1107}
1108
1109#[cfg(test)]
1110#[rustfmt::skip]
1111mod test_token {
1112    use libtw2_warn::Ignore;
1113    use quickcheck::quickcheck;
1114    use super::MAX_PACKETSIZE;
1115    use super::Packet;
1116    use super::PacketReadError::*;
1117    use super::PacketReadError;
1118    use super::Warning::*;
1119    use super::Warning;
1120
1121    fn assert_warn(input: &[u8], warning: Warning) {
1122        super::test::assert_warn(true, false, input, warning);
1123    }
1124
1125    fn assert_warn_no_heur(input: &[u8], warning: Warning) {
1126        super::test::assert_warn(true, true, input, warning);
1127    }
1128
1129    fn assert_no_warn(input: &[u8]) {
1130        super::test::assert_no_warn(true, false, input);
1131    }
1132
1133    fn assert_no_warn_no_heur(input: &[u8]) {
1134        super::test::assert_no_warn(true, true, input);
1135    }
1136
1137    fn assert_err(input: &[u8], error: PacketReadError) {
1138        super::test::assert_err(true, false, input, error);
1139    }
1140
1141    fn assert_err_no_heur(input: &[u8], error: PacketReadError) {
1142        super::test::assert_err(true, true, input, error);
1143    }
1144
1145    #[test] fn w_chp() { assert_warn(b"\x00\x00\x01\x00\xf0\x12\x34\x56\x78", ChunkHeaderPadding) }
1146    #[test] fn w_chs1() { assert_warn(b"\x00\x00\x01\x40\x20\x00\x12\x34\x56\x78", ChunkHeaderSequence) }
1147    #[test] fn w_chs2() { assert_warn(b"\x00\x00\x01\x40\x10\x00\x12\x34\x56\x78", ChunkHeaderSequence) }
1148    #[test] fn w_chs3() { assert_no_warn(b"\x00\x00\x01\x40\x70\xcf\x12\x34\x56\x78") }
1149    #[test] fn w_cud1() { assert_warn(b"\x00\x00\x00\xff\x12\x34\x56\x78", ChunksUnknownData) }
1150    #[test] fn w_cud2() { assert_warn(b"\x00\x00\x01\x00\x00\x00\x12\x34\x56\x78", ChunksUnknownData) }
1151    #[test] fn w_cud3() { assert_no_warn(b"\x00\x00\x01\x00\x00\x12\x34\x56\x78") }
1152    #[test] fn w_cnc1() { assert_warn_no_heur(b"\x00\x00\x01\x12\x34\x56\x78", ChunksNumChunks) }
1153    #[test] fn w_cnc2() { assert_warn(b"\x00\x00\x00\x00\x00\x12\x34\x56\x78", ChunksNumChunks) }
1154    #[test] fn w_cnc_() { assert_warn(b"\x00\x00\x00\x12\x34\x56\x78", ChunksNoChunks) }
1155    #[test] fn w_cp1() { assert_warn(b"xe\x01\x02\x03\x04\x12\x34\x56\x78", ConnlessPadding) }
1156    #[test] fn w_cp2() { assert_warn(b"\xff\xff\xff\xff\xff\xfe\x12\x34\x56\x78", ConnlessPadding) }
1157    #[test] fn w_cp3() { assert_warn(b"\x7f\xff\xff\xff\xff\xff\x12\x34\x56\x78", ConnlessPadding) }
1158    #[test] fn w_cp4() { assert_no_warn(b"\xff\xff\xff\xff\xff\xff\x12\x34\x56\x78") }
1159    #[test] fn w_ced1() { assert_warn(b"\x10\x00\x00\x00\x00\x12\x34\x56\x78", ControlExcessData) }
1160    #[test] fn w_ced2() { assert_warn(b"\x10\x00\x00\x04\x00\x00\x12\x34\x56\x78", ControlExcessData) }
1161    #[test] fn w_cf1() { assert_warn(b"\x90\x00\x00\xb9\x3c\xd2\x85\x6b\x53\xdc\x00", ControlFlags) }
1162    #[test] fn w_cf2() { assert_warn(b"\x50\x00\x00\x00\x12\x34\x56\x78", ControlFlags) }
1163    #[test] fn w_cnt1() { assert_warn(b"\x10\x00\x00\x04\x01\x12\x34\x56\x78", ControlNulTermination) }
1164    #[test] fn w_cnt2() { assert_no_warn_no_heur(b"\x10\x00\x00\x04\x12\x34\x56\x78") }
1165    #[test] fn w_cnc() { assert_warn(b"\x10\x00\xff\x00\x12\x34\x56\x78", ControlNumChunks) }
1166    #[test] fn w_php1() { assert_warn(b"\x08\x00\x00\x12\x34\x56\x78", PacketHeaderPadding) }
1167    #[test] fn w_php2() { assert_warn(b"\x04\x00\x00\x12\x34\x56\x78", PacketHeaderPadding) }
1168
1169    #[test] fn e_cm() { assert_err_no_heur(b"\x10\x00\x00\x12\x34\x56\x78", ControlMissing) }
1170    #[test] fn e_sc() { assert_err(b"\xff\xff\xff", ShortConnless) }
1171    #[test] fn e_tl() { assert_err(&[0; MAX_PACKETSIZE+1], TooLong) }
1172    #[test] fn e_tm1() { assert_err_no_heur(b"\x00\x00\x00", TokenMissing) }
1173    #[test] fn e_tm2() { assert_err_no_heur(b"\x00\x00\x00\x12", TokenMissing) }
1174    #[test] fn e_tm3() { assert_err_no_heur(b"\x00\x00\x00\x12\x34\x56", TokenMissing) }
1175    #[test] fn e_ts1() { assert_err(b"\x00\x00", TooShort) }
1176    #[test] fn e_ts2() { assert_err(b"", TooShort) }
1177    #[test] fn e_uc1() { assert_err(b"\x10\x00\x00\x05\x12\x34\x56\x78", UnknownControl) }
1178    #[test] fn e_uc2() { assert_err(b"\x10\x00\x00\xff\x12\x34\x56\x78", UnknownControl) }
1179    #[test] fn e_c() { assert_err(b"\x80\x00\x00", Compression) }
1180
1181    quickcheck! {
1182        fn packet_read_no_panic(data: Vec<u8>) -> bool {
1183            let mut buffer = [0; MAX_PACKETSIZE];
1184            let _ = Packet::read(&mut Ignore, &data, Some(true), &mut buffer[..]);
1185            true
1186        }
1187    }
1188}