1
1
mirror of https://github.com/wez/wezterm.git synced 2025-01-05 12:15:23 +03:00

mux: improve error handling around ssh bootstrap and tls setup

Work a bit harder to capture more context and log it.

As of this commit, windows is now able to use openssl to connect to
a remote mux over TLS.
This commit is contained in:
Wez Furlong 2020-02-02 12:14:22 -08:00
parent 6d2695d650
commit fa4bddd943
2 changed files with 55 additions and 23 deletions

View File

@ -143,7 +143,9 @@ fn client_thread(
}
let mut poll_array = [rx.as_poll_fd(), reconnectable.stream().as_poll_fd()];
poll_for_read(&mut poll_array);
if !reconnectable.stream().has_read_buffered() {
poll_for_read(&mut poll_array);
}
if poll_array[1].revents != 0 || reconnectable.stream().has_read_buffered() {
// When TLS is enabled on a stream, it may require a mixture of
@ -480,10 +482,29 @@ impl Reconnectable {
// obtain client credentials that we can use for tls.
let cmd = format!("{} cli tlscreds", Self::wezterm_bin_path());
ui.output_str(&format!("Running: {}\n", cmd));
chan.exec(&cmd)?;
let creds = match Pdu::decode(chan)?.pdu {
chan.exec(&cmd)
.with_context(|| format!("executing `{}` on remote host", cmd))?;
// stdout holds an encoded pdu
let mut buf = Vec::new();
chan.read_to_end(&mut buf)
.context("reading tlscreds response to buffer")?;
// stderr is ideally empty
let mut err = String::new();
chan.stderr()
.read_to_string(&mut err)
.context("reading tlscreds stderr")?;
if !err.is_empty() {
log::error!("remote: `{}` stderr -> `{}`", cmd, err);
}
let creds = match Pdu::decode(buf.as_slice())
.with_context(|| format!("reading tlscreds response. stderr={}", err))?
.pdu
{
Pdu::GetTlsCredsResponse(creds) => creds,
_ => bail!("unexpected response to tlscreds"),
_ => bail!("unexpected response to tlscreds, stderr={}", err),
};
// Save the credentials to disk, as that is currently the easiest

View File

@ -15,7 +15,7 @@ use crate::mux::domain::DomainId;
use crate::mux::renderable::{RenderableDimensions, StableCursorPosition};
use crate::mux::tab::TabId;
use crate::mux::window::WindowId;
use anyhow::{bail, Error};
use anyhow::{bail, Context as _, Error};
use leb128;
use log::debug;
use portable_pty::{CommandBuilder, PtySize};
@ -59,7 +59,7 @@ fn encode_raw<W: std::io::Write>(
data: &[u8],
is_compressed: bool,
mut w: W,
) -> Result<usize, std::io::Error> {
) -> anyhow::Result<usize> {
let len = data.len() + encoded_length(ident) + encoded_length(serial);
let masked_len = if is_compressed {
(len as u64) | COMPRESSED_MASK
@ -72,9 +72,9 @@ fn encode_raw<W: std::io::Write>(
// the header portion to go out in a single packet)
let mut buffer = Vec::with_capacity(len + encoded_length(masked_len));
leb128::write::unsigned(&mut buffer, masked_len)?;
leb128::write::unsigned(&mut buffer, serial)?;
leb128::write::unsigned(&mut buffer, ident)?;
leb128::write::unsigned(&mut buffer, masked_len).context("writing pdu len")?;
leb128::write::unsigned(&mut buffer, serial).context("writing pdu serial")?;
leb128::write::unsigned(&mut buffer, ident).context("writing pdu ident")?;
buffer.extend_from_slice(data);
if is_compressed {
@ -83,17 +83,19 @@ fn encode_raw<W: std::io::Write>(
metrics::value!("pdu.encode.size", buffer.len() as u64);
}
w.write_all(&buffer)?;
w.write_all(&buffer).context("writing pdu data buffer")?;
Ok(buffer.len())
}
/// Read a single leb128 encoded value from the stream
fn read_u64<R: std::io::Read>(mut r: R) -> Result<u64, std::io::Error> {
leb128::read::unsigned(&mut r).map_err(|err| match err {
leb128::read::Error::IoError(ioerr) => ioerr,
err => std::io::Error::new(std::io::ErrorKind::Other, format!("{}", err)),
})
fn read_u64<R: std::io::Read>(mut r: R) -> anyhow::Result<u64> {
leb128::read::unsigned(&mut r)
.map_err(|err| match err {
leb128::read::Error::IoError(ioerr) => anyhow::Error::new(ioerr),
err => anyhow::Error::new(err),
})
.context("reading leb128")
}
#[derive(Debug)]
@ -106,15 +108,15 @@ struct Decoded {
/// Decode a frame.
/// See encode_raw() for the frame format.
fn decode_raw<R: std::io::Read>(mut r: R) -> Result<Decoded, std::io::Error> {
let len = read_u64(r.by_ref())?;
fn decode_raw<R: std::io::Read>(mut r: R) -> anyhow::Result<Decoded> {
let len = read_u64(r.by_ref()).context("reading PDU length")?;
let (len, is_compressed) = if (len & COMPRESSED_MASK) != 0 {
(len & !COMPRESSED_MASK, true)
} else {
(len, false)
};
let serial = read_u64(r.by_ref())?;
let ident = read_u64(r.by_ref())?;
let serial = read_u64(r.by_ref()).context("reading PDU serial")?;
let ident = read_u64(r.by_ref()).context("reading PDU ident")?;
let data_len = len as usize - (encoded_length(ident) + encoded_length(serial));
if is_compressed {
@ -124,7 +126,12 @@ fn decode_raw<R: std::io::Read>(mut r: R) -> Result<Decoded, std::io::Error> {
}
let mut data = vec![0u8; data_len];
r.read_exact(&mut data)?;
r.read_exact(&mut data).with_context(|| {
format!(
"reading {} bytes of data for PDU of length {} with serial={} ident={}",
data_len, len, serial, ident
)
})?;
Ok(Decoded {
ident,
serial,
@ -211,7 +218,7 @@ macro_rules! pdu {
}
pub fn decode<R: std::io::Read>(r:R) -> Result<DecodedPdu, Error> {
let decoded = decode_raw(r)?;
let decoded = decode_raw(r).context("decoding a PDU")?;
match decoded.ident {
$(
$vers => {
@ -290,13 +297,15 @@ impl Pdu {
Ok(Some(decoded))
}
Err(err) => {
if let Some(ioerr) = err.downcast_ref::<std::io::Error>() {
if let Some(ioerr) = err.root_cause().downcast_ref::<std::io::Error>() {
match ioerr.kind() {
std::io::ErrorKind::UnexpectedEof | std::io::ErrorKind::WouldBlock => {
return Ok(None);
}
_ => {}
}
} else {
log::error!("not an ioerror in stream_decode: {:?}", err);
}
Err(err)
}
@ -308,7 +317,9 @@ impl Pdu {
buffer: &mut Vec<u8>,
) -> anyhow::Result<Option<DecodedPdu>> {
loop {
if let Some(decoded) = Self::stream_decode(buffer)? {
if let Some(decoded) =
Self::stream_decode(buffer).context("stream_decode of buffer for PDU")?
{
return Ok(Some(decoded));
}