Skip to content

Commit d26502c

Browse files
authored
fix: Check for ErrorKind::WouldBlock in LazyConfigAcceptor (#48)
1 parent 330d287 commit d26502c

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

src/lib.rs

Lines changed: 22 additions & 5 deletions
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::InvalidData, 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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,5 +290,37 @@ async fn acceptor_alert() {
290290
assert_eq!(received, [0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x46]);
291291
}
292292

293+
#[tokio::test]
294+
async fn lazy_config_acceptor_alert() {
295+
// Intentionally small so that we have to call alert.write several times
296+
let (mut cstream, sstream) = tokio::io::duplex(2);
297+
298+
let (tx, rx) = oneshot::channel();
299+
300+
tokio::spawn(async move {
301+
// This is write instead of write_all because of the short duplex size, which is necessarily
302+
// symmetrical. We never finish writing because the LazyConfigAcceptor returns an error
303+
let _ = cstream.write(b"not tls").await;
304+
let mut buf = Vec::new();
305+
cstream.read_to_end(&mut buf).await.unwrap();
306+
tx.send(buf).unwrap();
307+
});
308+
309+
let acceptor = LazyConfigAcceptor::new(rustls::server::Acceptor::default(), sstream);
310+
311+
let Ok(accept_result) = time::timeout(Duration::from_secs(3), acceptor).await else {
312+
panic!("timeout");
313+
};
314+
315+
assert!(accept_result.is_err());
316+
317+
let Ok(Ok(received)) = time::timeout(Duration::from_secs(3), rx).await else {
318+
panic!("failed to receive");
319+
};
320+
321+
let fatal_alert_decode_error = b"\x15\x03\x03\x00\x02\x02\x32";
322+
assert_eq!(received, fatal_alert_decode_error)
323+
}
324+
293325
// Include `utils` module
294326
include!("utils.rs");

0 commit comments

Comments
 (0)