1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read};

use rustls::{
    internal::msgs::{
        deframer::MessageDeframer,
        handshake::{ClientHelloPayload as ClientHelloPayloadInner, HandshakePayload},
        message::{Message, MessagePayload},
    },
    ContentType, Error as RustlsError, HandshakeType,
};

//
//
//
pub mod client_hello;
pub use client_hello::ClientHello;

//
//
//
pub struct Parser {
    message_deframer: MessageDeframer,
}

impl Default for Parser {
    fn default() -> Self {
        Self::new()
    }
}

impl Parser {
    pub fn new() -> Self {
        Self {
            message_deframer: MessageDeframer::new(),
        }
    }

    pub fn parse(&mut self, rd: &mut dyn Read) -> Result<Option<ClientHelloPayload>, ParseError> {
        let n = self
            .message_deframer
            .read(rd)
            .map_err(ParseError::IoError)?;

        match self
            .message_deframer
            .pop()
            .map_err(ParseError::RustlsError)?
        {
            Some(opaque_message) => {
                let plain_message = opaque_message.into_plain_message();
                let plain_message_typ = plain_message.typ.to_owned();
                let message = Message::try_from(plain_message).map_err(ParseError::RustlsError)?;
                match &message.payload {
                    MessagePayload::Handshake { parsed, encoded: _ } => match &parsed.payload {
                        HandshakePayload::ClientHello(chp) => {
                            let inner = ClientHelloPayloadInner {
                                client_version: chp.client_version,
                                random: chp.random.to_owned(),
                                session_id: chp.session_id,
                                cipher_suites: chp.cipher_suites.to_owned(),
                                compression_methods: chp.compression_methods.to_owned(),
                                extensions: chp.extensions.to_owned(),
                            };
                            Ok(Some(ClientHelloPayload(inner)))
                        }
                        _ => Err(ParseError::RustlsError(
                            RustlsError::InappropriateHandshakeMessage {
                                expect_types: vec![HandshakeType::ClientHello],
                                got_type: parsed.typ,
                            },
                        )),
                    },
                    _ => Err(ParseError::RustlsError(RustlsError::InappropriateMessage {
                        expect_types: vec![ContentType::Handshake],
                        got_type: plain_message_typ,
                    })),
                }
            }
            None => {
                if n == 0 {
                    Err(ParseError::IoError(IoErrorKind::UnexpectedEof.into()))
                } else {
                    Ok(None)
                }
            }
        }
    }
}

#[derive(Debug)]
pub enum ParseError {
    IoError(IoError),
    RustlsError(RustlsError),
}
impl core::fmt::Display for ParseError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{self:?}")
    }
}
impl std::error::Error for ParseError {}

//
//
//
#[derive(Debug)]
pub struct ClientHelloPayload(pub ClientHelloPayloadInner);

impl core::ops::Deref for ClientHelloPayload {
    type Target = ClientHelloPayloadInner;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl core::ops::DerefMut for ClientHelloPayload {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl ClientHelloPayload {
    pub fn client_hello(&self) -> Result<ClientHello<'_>, RustlsError> {
        use rustls::internal::msgs::handshake::ConvertServerNameList as _;

        Ok(ClientHello::new(
            self.get_sni_extension()
                .and_then(|x| x.get_single_hostname())
                .map(|x| x.to_owned()),
            self.get_sigalgs_extension().ok_or_else(|| {
                // https://github.com/rustls/rustls/blob/v/0.20.8/rustls/src/server/hs.rs#L512
                RustlsError::PeerIncompatibleError(
                    "client didn't describe signature schemes".into(),
                )
            })?,
            self.get_alpn_extension(),
            self.cipher_suites.as_ref(),
        ))
    }
}