Skip to content

Commit 263fd4b

Browse files
committed
fix: Check for ErrorKind::WouldBlock in LazyConfigAcceptor
1 parent 4a4a448 commit 263fd4b

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

src/lib.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ use std::sync::Arc;
4747
use std::task::{Context, Poll};
4848

4949
pub use rustls;
50+
use rustls::server::AcceptedAlert;
5051
use rustls::{ClientConfig, ClientConnection, CommonState, ServerConfig, ServerConnection};
5152
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
5253

@@ -195,6 +196,7 @@ impl TlsAcceptor {
195196
pub struct LazyConfigAcceptor<IO> {
196197
acceptor: rustls::server::Acceptor,
197198
io: Option<IO>,
199+
alert: Option<(rustls::Error, AcceptedAlert)>,
198200
}
199201

200202
impl<IO> LazyConfigAcceptor<IO>
@@ -206,6 +208,7 @@ where
206208
Self {
207209
acceptor,
208210
io: Some(io),
211+
alert: None,
209212
}
210213
}
211214

@@ -274,6 +277,22 @@ where
274277
}
275278
};
276279

280+
if let Some((err, mut alert)) = this.alert.take() {
281+
match alert.write(&mut common::SyncWriteAdapter { io, cx }) {
282+
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
283+
this.alert = Some((err, alert));
284+
return Poll::Pending;
285+
}
286+
Ok(0) | Err(_) => {
287+
return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, err)))
288+
}
289+
Ok(_) => {
290+
this.alert = Some((err, alert));
291+
continue;
292+
}
293+
};
294+
}
295+
277296
let mut reader = common::SyncReadAdapter { io, cx };
278297
match this.acceptor.read_tls(&mut reader) {
279298
Ok(0) => return Err(io::ErrorKind::UnexpectedEof.into()).into(),
@@ -287,11 +306,9 @@ where
287306
let io = this.io.take().unwrap();
288307
return Poll::Ready(Ok(StartHandshake { accepted, io }));
289308
}
290-
Ok(None) => continue,
291-
Err((err, mut alert)) => {
292-
let mut writer = common::SyncWriteAdapter { io, cx };
293-
let _ = alert.write(&mut writer); // best effort
294-
return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, err)));
309+
Ok(None) => {}
310+
Err((err, alert)) => {
311+
this.alert = Some((err, alert));
295312
}
296313
}
297314
}

tests/test.rs

+32
Original file line numberDiff line numberDiff line change
@@ -241,5 +241,37 @@ async fn lazy_config_acceptor_take_io() -> Result<(), rustls::Error> {
241241
Ok(())
242242
}
243243

244+
#[tokio::test]
245+
async fn lazy_config_acceptor_alert() {
246+
// Intentionally small so that we have to call alert.write several times
247+
let (mut cstream, sstream) = tokio::io::duplex(2);
248+
249+
let (tx, rx) = oneshot::channel();
250+
251+
tokio::spawn(async move {
252+
// This is write instead of write_all because of the short duplex size, which is necessarily
253+
// symmetrical. We never finish writing because the LazyConfigAcceptor returns an error
254+
let _ = cstream.write(b"not tls").await;
255+
let mut buf = Vec::new();
256+
cstream.read_to_end(&mut buf).await.unwrap();
257+
tx.send(buf).unwrap();
258+
});
259+
260+
let acceptor = LazyConfigAcceptor::new(rustls::server::Acceptor::default(), sstream);
261+
262+
let Ok(accept_result) = time::timeout(Duration::from_secs(3), acceptor).await else {
263+
panic!("timeout");
264+
};
265+
266+
assert!(accept_result.is_err());
267+
268+
let Ok(Ok(received)) = time::timeout(Duration::from_secs(3), rx).await else {
269+
panic!("failed to receive");
270+
};
271+
272+
let fatal_alert_decode_error = b"\x15\x03\x03\x00\x02\x02\x32";
273+
assert_eq!(received, fatal_alert_decode_error)
274+
}
275+
244276
// Include `utils` module
245277
include!("utils.rs");

0 commit comments

Comments
 (0)