Handle malformed HEADERS

This commit is contained in:
Carl Lerche
2017-08-25 13:00:42 -07:00
parent 9d45255c75
commit 14f35f1be6
12 changed files with 155 additions and 54 deletions

View File

@@ -186,14 +186,16 @@ impl Headers {
-> Result<(), Error>
{
let mut reg = false;
let mut err = false;
let mut malformed = false;
macro_rules! set_pseudo {
($field:ident, $val:expr) => {{
if reg {
err = true;
trace!("load_hpack; header malformed -- pseudo not at head of block");
malformed = true;
} else if self.pseudo.$field.is_some() {
err = true;
trace!("load_hpack; header malformed -- repeated pseudo");
malformed = true;
} else {
self.pseudo.$field = Some($val);
}
@@ -212,8 +214,19 @@ impl Headers {
match header {
Field { name, value } => {
reg = true;
self.fields.append(name, value);
// Connection level header fields are not supported and must
// result in a protocol error.
if name == header::CONNECTION {
trace!("load_hpack; connection level header");
malformed = true;
} else if name == header::TE && value != "trailers" {
trace!("load_hpack; TE header not set to trailers; val={:?}", value);
malformed = true;
} else {
reg = true;
self.fields.append(name, value);
}
}
Authority(v) => set_pseudo!(authority, v),
Method(v) => set_pseudo!(method, v),
@@ -228,9 +241,9 @@ impl Headers {
return Err(e.into());
}
if err {
trace!("repeated pseudo");
return Err(hpack::DecoderError::RepeatedPseudo.into());
if malformed {
trace!("malformed message");
return Err(Error::MalformedMessage.into());
}
Ok(())

View File

@@ -171,6 +171,9 @@ pub enum Error {
/// identifier other than zero.
InvalidStreamId,
/// A request or response is malformed.
MalformedMessage,
/// An invalid stream dependency ID was provided
///
/// This is returend if a HEADERS or PRIORITY frame is received with an

View File

@@ -35,7 +35,6 @@ pub enum DecoderError {
IntegerUnderflow,
IntegerOverflow,
StringUnderflow,
RepeatedPseudo,
UnexpectedEndOfStream,
}

View File

@@ -53,9 +53,9 @@ impl<T, B> futures::Stream for Codec<T, B>
where T: AsyncRead,
{
type Item = Frame;
type Error = ConnectionError;
type Error = ProtoError;
fn poll(&mut self) -> Poll<Option<Frame>, ConnectionError> {
fn poll(&mut self) -> Poll<Option<Frame>, Self::Error> {
self.inner.poll()
}
}

View File

@@ -123,6 +123,7 @@ impl<T, P, B> Connection<T, P, B>
fn poll2(&mut self) -> Poll<(), ConnectionError> {
use frame::Frame::*;
use proto::ProtoError::*;
loop {
// First, ensure that the `Connection` is able to receive a frame
@@ -130,13 +131,29 @@ impl<T, P, B> Connection<T, P, B>
trace!("polling codec");
let frame = match try!(self.codec.poll()) {
Async::Ready(frame) => frame,
Async::NotReady => {
let frame = match self.codec.poll() {
// Receive a frame
Ok(Async::Ready(frame)) => frame,
// Socket not ready, try to flush any pending data
Ok(Async::NotReady) => {
// Flush any pending writes
let _ = try!(self.poll_complete());
return Ok(Async::NotReady);
}
// Connection level error, set GO_AWAY and close connection
Err(Connection(reason)) => {
return Err(ConnectionError::Proto(reason));
}
// Stream level error, reset the stream
Err(Stream { id, reason }) => {
trace!("stream level error; id={:?}; reason={:?}", id, reason);
self.streams.send_reset::<P>(id, reason);
continue;
}
// I/O error, nothing more can be done
Err(Io(err)) => {
return Err(err.into());
}
};
debug!("recv; frame={:?}", frame);

View File

@@ -36,6 +36,8 @@ struct Partial {
#[derive(Debug)]
enum Continuable {
Headers(frame::Headers),
// Decode the Continuation frame but ignore it...
// Ignore(StreamId),
// PushPromise(frame::PushPromise),
}
@@ -52,14 +54,16 @@ impl<T> FramedRead<T> {
// TODO: Is this needed?
}
fn decode_frame(&mut self, mut bytes: BytesMut) -> Result<Option<Frame>, ConnectionError> {
fn decode_frame(&mut self, mut bytes: BytesMut) -> Result<Option<Frame>, ProtoError> {
use self::ProtoError::*;
trace!("decoding frame from {}B", bytes.len());
// Parse the head
let head = frame::Head::parse(&bytes);
if self.partial.is_some() && head.kind() != Kind::Continuation {
return Err(ProtocolError.into());
return Err(Connection(ProtocolError));
}
let kind = head.kind();
@@ -68,17 +72,26 @@ impl<T> FramedRead<T> {
let frame = match kind {
Kind::Settings => {
frame::Settings::load(head, &bytes[frame::HEADER_LEN..])?.into()
let res = frame::Settings::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Ping => {
frame::Ping::load(head, &bytes[frame::HEADER_LEN..])?.into()
let res = frame::Ping::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::WindowUpdate => {
frame::WindowUpdate::load(head, &bytes[frame::HEADER_LEN..])?.into()
let res = frame::WindowUpdate::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Data => {
let _ = bytes.split_to(frame::HEADER_LEN);
frame::Data::load(head, bytes.freeze())?.into()
let res = frame::Data::load(head, bytes.freeze());
// TODO: Should this always be connection level? Probably not...
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Headers => {
// Drop the frame header
@@ -86,11 +99,24 @@ impl<T> FramedRead<T> {
let _ = bytes.split_to(frame::HEADER_LEN);
// Parse the header frame w/o parsing the payload
let (mut headers, payload) = frame::Headers::load(head, bytes)?;
let (mut headers, payload) = match frame::Headers::load(head, bytes) {
Ok(res) => res,
Err(_) => unimplemented!(),
};
if headers.is_end_headers() {
// Load the HPACK encoded headers & return the frame
headers.load_hpack(payload, &mut self.hpack)?;
match headers.load_hpack(payload, &mut self.hpack) {
Ok(_) => {}
Err(frame::Error::MalformedMessage) => {
return Err(Stream {
id: head.stream_id(),
reason: ProtocolError,
});
}
Err(_) => return Err(Connection(ProtocolError)),
}
headers.into()
} else {
// Defer loading the frame
@@ -103,16 +129,20 @@ impl<T> FramedRead<T> {
}
}
Kind::Reset => {
frame::Reset::load(head, &bytes[frame::HEADER_LEN..])?.into()
let res = frame::Reset::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::GoAway => {
frame::GoAway::load(&bytes[frame::HEADER_LEN..])?.into()
let res = frame::GoAway::load(&bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::PushPromise => {
frame::PushPromise::load(head, &bytes[frame::HEADER_LEN..])?.into()
let res = frame::PushPromise::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Priority => {
frame::Priority::load(head, &bytes[frame::HEADER_LEN..])?.into()
let res = frame::Priority::load(head, &bytes[frame::HEADER_LEN..]);
res.map_err(|_| Connection(ProtocolError))?.into()
}
Kind::Continuation => {
// TODO: Un-hack this
@@ -120,7 +150,7 @@ impl<T> FramedRead<T> {
let mut partial = match self.partial.take() {
Some(partial) => partial,
None => return Err(ProtocolError.into()),
None => return Err(Connection(ProtocolError)),
};
// Extend the buf
@@ -135,10 +165,20 @@ impl<T> FramedRead<T> {
Continuable::Headers(mut frame) => {
// The stream identifiers must match
if frame.stream_id() != head.stream_id() {
return Err(ProtocolError.into());
return Err(Connection(ProtocolError));
}
match frame.load_hpack(partial.buf, &mut self.hpack) {
Ok(_) => {}
Err(frame::Error::MalformedMessage) => {
return Err(Stream {
id: head.stream_id(),
reason: ProtocolError,
});
}
Err(_) => return Err(Connection(ProtocolError)),
}
frame.load_hpack(partial.buf, &mut self.hpack)?;
frame.into()
}
}
@@ -165,9 +205,9 @@ impl<T> futures::Stream for FramedRead<T>
where T: AsyncRead,
{
type Item = Frame;
type Error = ConnectionError;
type Error = ProtoError;
fn poll(&mut self) -> Poll<Option<Frame>, ConnectionError> {
fn poll(&mut self) -> Poll<Option<Frame>, Self::Error> {
loop {
trace!("poll");
let bytes = match try_ready!(self.inner.poll()) {

View File

@@ -26,6 +26,8 @@ use bytes::{Buf, IntoBuf};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::length_delimited;
use std::io;
/// Either a Client or a Server
pub trait Peer {
/// Message type sent into the transport
@@ -50,6 +52,17 @@ pub type PingPayload = [u8; 8];
pub type WindowSize = u32;
/// Errors that are received
#[derive(Debug)]
pub enum ProtoError {
Connection(Reason),
Stream {
id: StreamId,
reason: Reason,
},
Io(io::Error),
}
// Constants
pub const DEFAULT_INITIAL_WINDOW_SIZE: WindowSize = 65_535;
pub const MAX_WINDOW_SIZE: WindowSize = (1 << 31) - 1;
@@ -88,3 +101,11 @@ pub(crate) fn from_framed_write<T, P, B>(framed_write: FramedWrite<T, Prioritize
Connection::new(codec)
}
// ===== impl ProtoError =====
impl From<io::Error> for ProtoError {
fn from(src: io::Error) -> Self {
ProtoError::Io(src)
}
}

View File

@@ -1,4 +1,3 @@
use ConnectionError;
use frame::Ping;
use proto::*;

View File

@@ -1,4 +1,4 @@
use {frame, ConnectionError};
use frame;
use proto::*;
use futures::Sink;

View File

@@ -1,5 +1,4 @@
use {frame, ConnectionError};
use error::User::InactiveStreamId;
use proto::*;
use super::*;
@@ -101,27 +100,10 @@ impl<B> Send<B> where B: Buf {
Ok(())
}
/// This is called by the user to send a reset and should not be called
/// by internal state transitions. Use `reset_stream` for that.
pub fn send_reset(&mut self,
reason: Reason,
stream: &mut store::Ptr<B>,
task: &mut Option<Task>)
-> Result<(), ConnectionError>
{
if stream.state.is_closed() {
debug!("send_reset; invalid stream ID");
return Err(InactiveStreamId.into())
}
self.reset_stream(reason, stream, task);
Ok(())
}
fn reset_stream(&mut self,
reason: Reason,
stream: &mut store::Ptr<B>,
task: &mut Option<Task>)
{
if stream.state.is_reset() {
// Don't double reset
@@ -240,7 +222,7 @@ impl<B> Send<B> where B: Buf {
{
if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
debug!("recv_stream_window_update !!; err={:?}", e);
self.reset_stream(FlowControlError.into(), stream, task);
self.send_reset(FlowControlError.into(), stream, task);
}
Ok(())

View File

@@ -312,6 +312,33 @@ impl<B> Streams<B>
key: key,
})
}
pub fn send_reset<P: Peer>(&mut self, id: StreamId, reason: Reason) {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;
let key = match me.store.find_entry(id) {
Entry::Occupied(e) => e.key(),
Entry::Vacant(e) => {
match me.actions.recv.open::<P>(id) {
Ok(Some(stream_id)) => {
let stream = Stream::new(
stream_id, 0, 0);
e.insert(stream)
}
_ => return,
}
}
};
let stream = me.store.resolve(key);
me.actions.transition::<P, _, _>(stream, move |actions, stream| {
actions.send.send_reset(reason, stream, &mut actions.task)
})
}
}
// ===== impl StreamRef =====
@@ -367,7 +394,7 @@ impl<B> StreamRef<B>
me.actions.recv.take_request(&mut stream)
}
pub fn send_reset<P: Peer>(&mut self, reason: Reason) -> Result<(), ConnectionError> {
pub fn send_reset<P: Peer>(&mut self, reason: Reason) {
let mut me = self.inner.lock().unwrap();
let me = &mut *me;

View File

@@ -191,7 +191,7 @@ impl<B: IntoBuf> Stream<B> {
self.inner.send_trailers::<Peer>(trailers)
}
pub fn send_reset(mut self, reason: Reason) -> Result<(), ConnectionError> {
pub fn send_reset(mut self, reason: Reason) {
self.inner.send_reset::<Peer>(reason)
}
}