libtw2_net/
protocol.rs

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