tls_client_hello_parser/
lib.rs1use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read};
2
3use rustls::{
4 internal::msgs::{
5 deframer::MessageDeframer,
6 handshake::{ClientHelloPayload as ClientHelloPayloadInner, HandshakePayload},
7 message::{Message, MessagePayload},
8 },
9 ContentType, Error as RustlsError, HandshakeType,
10};
11
12pub mod client_hello;
16pub use client_hello::ClientHello;
17
18pub struct Parser {
22 message_deframer: MessageDeframer,
23}
24
25impl Default for Parser {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl Parser {
32 pub fn new() -> Self {
33 Self {
34 message_deframer: MessageDeframer::new(),
35 }
36 }
37
38 pub fn parse(&mut self, rd: &mut dyn Read) -> Result<Option<ClientHelloPayload>, ParseError> {
39 let n = self
40 .message_deframer
41 .read(rd)
42 .map_err(ParseError::IoError)?;
43
44 match self
45 .message_deframer
46 .pop()
47 .map_err(ParseError::RustlsError)?
48 {
49 Some(opaque_message) => {
50 let plain_message = opaque_message.into_plain_message();
51 let plain_message_typ = plain_message.typ.to_owned();
52 let message = Message::try_from(plain_message).map_err(ParseError::RustlsError)?;
53 match &message.payload {
54 MessagePayload::Handshake { parsed, encoded: _ } => match &parsed.payload {
55 HandshakePayload::ClientHello(chp) => {
56 let inner = ClientHelloPayloadInner {
57 client_version: chp.client_version,
58 random: chp.random.to_owned(),
59 session_id: chp.session_id,
60 cipher_suites: chp.cipher_suites.to_owned(),
61 compression_methods: chp.compression_methods.to_owned(),
62 extensions: chp.extensions.to_owned(),
63 };
64 Ok(Some(ClientHelloPayload(inner)))
65 }
66 _ => Err(ParseError::RustlsError(
67 RustlsError::InappropriateHandshakeMessage {
68 expect_types: vec![HandshakeType::ClientHello],
69 got_type: parsed.typ,
70 },
71 )),
72 },
73 _ => Err(ParseError::RustlsError(RustlsError::InappropriateMessage {
74 expect_types: vec![ContentType::Handshake],
75 got_type: plain_message_typ,
76 })),
77 }
78 }
79 None => {
80 if n == 0 {
81 Err(ParseError::IoError(IoErrorKind::UnexpectedEof.into()))
82 } else {
83 Ok(None)
84 }
85 }
86 }
87 }
88}
89
90#[derive(Debug)]
91pub enum ParseError {
92 IoError(IoError),
93 RustlsError(RustlsError),
94}
95impl core::fmt::Display for ParseError {
96 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
97 write!(f, "{self:?}")
98 }
99}
100impl std::error::Error for ParseError {}
101
102#[derive(Debug)]
106pub struct ClientHelloPayload(pub ClientHelloPayloadInner);
107
108impl core::ops::Deref for ClientHelloPayload {
109 type Target = ClientHelloPayloadInner;
110
111 fn deref(&self) -> &Self::Target {
112 &self.0
113 }
114}
115impl core::ops::DerefMut for ClientHelloPayload {
116 fn deref_mut(&mut self) -> &mut Self::Target {
117 &mut self.0
118 }
119}
120
121impl ClientHelloPayload {
122 pub fn client_hello(&self) -> Result<ClientHello<'_>, RustlsError> {
123 use rustls::internal::msgs::handshake::ConvertServerNameList as _;
124
125 Ok(ClientHello::new(
126 self.get_sni_extension()
127 .and_then(|x| x.get_single_hostname())
128 .map(|x| x.to_owned()),
129 self.get_sigalgs_extension().ok_or_else(|| {
130 RustlsError::PeerIncompatibleError(
132 "client didn't describe signature schemes".into(),
133 )
134 })?,
135 self.get_alpn_extension(),
136 self.cipher_suites.as_ref(),
137 ))
138 }
139}