ipp_proto/
parser.rs

1//!
2//! IPP stream parser
3//!
4use std::{
5    fmt,
6    io::{self, Read},
7};
8
9use byteorder::{BigEndian, ReadBytesExt};
10use futures::{try_ready, Async, Future, Poll, Stream};
11use log::{debug, error};
12use num_traits::FromPrimitive;
13
14use crate::{ipp::*, IppAttribute, IppAttributeGroup, IppAttributes, IppHeader, IppReadExt, IppValue, PayloadKind};
15
16/// Parse error enum
17#[derive(Debug)]
18pub enum ParseError {
19    InvalidTag(u8),
20    InvalidVersion,
21    InvalidCollection,
22    Incomplete,
23    IOError(io::Error),
24}
25
26impl From<io::Error> for ParseError {
27    fn from(error: io::Error) -> Self {
28        match error.kind() {
29            io::ErrorKind::UnexpectedEof => ParseError::Incomplete,
30            _ => ParseError::IOError(error),
31        }
32    }
33}
34
35impl fmt::Display for ParseError {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        match self {
38            ParseError::InvalidTag(tag) => write!(f, "Invalid tag: {}", tag),
39            ParseError::InvalidVersion => write!(f, "Invalid IPP protocol version"),
40            ParseError::InvalidCollection => write!(f, "Invalid IPP collection"),
41            ParseError::Incomplete => write!(f, "Incomplete IPP payload"),
42            ParseError::IOError(err) => write!(f, "{}", err.to_string()),
43        }
44    }
45}
46
47impl std::error::Error for ParseError {}
48
49// create a single value from one-element list, list otherwise
50fn list_or_value(mut list: Vec<IppValue>) -> IppValue {
51    if list.len() == 1 {
52        list.remove(0)
53    } else {
54        IppValue::ListOf(list)
55    }
56}
57
58/// IPP parsing result
59pub struct IppParseResult {
60    pub header: IppHeader,
61    pub attributes: IppAttributes,
62    pub payload: Option<PayloadKind>,
63}
64
65impl IppParseResult {
66    fn new(header: IppHeader, attributes: IppAttributes) -> IppParseResult {
67        IppParseResult {
68            header,
69            attributes,
70            payload: None,
71        }
72    }
73}
74
75/// IPP parser implementation
76pub struct IppParser<'a> {
77    reader: &'a mut Read,
78    current_group: Option<IppAttributeGroup>,
79    last_name: Option<String>,
80    context: Vec<Vec<IppValue>>,
81    attributes: IppAttributes,
82}
83
84impl<'a> IppParser<'a> {
85    /// Create IPP parser using the given Read
86    pub fn new(reader: &'a mut Read) -> IppParser<'a> {
87        IppParser {
88            reader,
89            current_group: None,
90            last_name: None,
91            context: vec![vec![]],
92            attributes: IppAttributes::new(),
93        }
94    }
95
96    fn add_last_attribute(&mut self) {
97        if let Some(ref last_name) = self.last_name {
98            if let Some(val_list) = self.context.pop() {
99                if let Some(ref mut group) = self.current_group {
100                    group.attributes_mut().insert(
101                        last_name.clone(),
102                        IppAttribute::new(&last_name, list_or_value(val_list)),
103                    );
104                }
105            }
106            self.context.push(vec![]);
107        }
108    }
109
110    fn parse_delimiter(&mut self, tag: u8) -> Result<DelimiterTag, ParseError> {
111        debug!("Delimiter tag: {:0x}", tag);
112
113        let tag = DelimiterTag::from_u8(tag).ok_or_else(|| ParseError::InvalidTag(tag))?;
114        if tag == DelimiterTag::EndOfAttributes {
115            self.add_last_attribute();
116        }
117
118        if let Some(group) = self.current_group.take() {
119            self.attributes.groups_mut().push(group);
120        }
121
122        self.current_group = Some(IppAttributeGroup::new(tag));
123
124        Ok(tag)
125    }
126
127    fn parse_value(&mut self, tag: u8) -> Result<(), ParseError> {
128        // value tag
129        let namelen = self.reader.read_u16::<BigEndian>()?;
130        let name = self.reader.read_string(namelen as usize)?;
131        let value = IppValue::read(tag, &mut self.reader)?;
132
133        debug!("Value tag: {:0x}: {}: {}", tag, name, value);
134
135        if namelen > 0 {
136            // single attribute or begin of array
137            self.add_last_attribute();
138            // store it as a previous attribute
139            self.last_name = Some(name);
140        }
141        if tag == ValueTag::BegCollection as u8 {
142            // start new collection in the stack
143            debug!("Begin collection");
144            match value {
145                IppValue::Other { ref data, .. } if data.is_empty() => {}
146                _ => {
147                    error!("Invalid begin collection attribute");
148                    return Err(ParseError::InvalidCollection);
149                }
150            }
151            self.context.push(vec![]);
152        } else if tag == ValueTag::EndCollection as u8 {
153            // get collection from the stack and add it to the previous element
154            debug!("End collection");
155            match value {
156                IppValue::Other { ref data, .. } if data.is_empty() => {}
157                _ => {
158                    error!("Invalid end collection attribute");
159                    return Err(ParseError::InvalidCollection);
160                }
161            }
162            if let Some(arr) = self.context.pop() {
163                if let Some(val_list) = self.context.last_mut() {
164                    val_list.push(IppValue::Collection(arr));
165                }
166            }
167        } else if let Some(val_list) = self.context.last_mut() {
168            // add attribute to the current collection
169            val_list.push(value);
170        }
171        Ok(())
172    }
173
174    /// Parse IPP stream
175    pub fn parse(mut self) -> Result<IppParseResult, ParseError> {
176        let header = IppHeader::from_reader(self.reader)?;
177        debug!("IPP header: {:?}", header);
178
179        loop {
180            match self.reader.read_u8()? {
181                tag @ 0x01...0x05 => {
182                    if self.parse_delimiter(tag)? == DelimiterTag::EndOfAttributes {
183                        break;
184                    }
185                }
186                tag @ 0x10...0x4a => self.parse_value(tag)?,
187                tag => {
188                    return Err(ParseError::InvalidTag(tag));
189                }
190            }
191        }
192
193        Ok(IppParseResult::new(header, self.attributes))
194    }
195}
196
197enum AsyncParseState {
198    Headers(Vec<u8>),
199    Payload(IppParseResult),
200}
201
202/// Asynchronous IPP parser using Streams
203pub struct AsyncIppParser<I, E> {
204    state: AsyncParseState,
205    stream: Box<dyn Stream<Item = I, Error = E> + Send>,
206}
207
208impl<I, E> Future for AsyncIppParser<I, E>
209where
210    I: AsRef<[u8]>,
211    ParseError: From<E>,
212{
213    type Item = IppParseResult;
214    type Error = ParseError;
215
216    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
217        while let Some(item) = try_ready!(self.stream.poll()) {
218            match self.state {
219                AsyncParseState::Headers(ref mut buffer) => {
220                    buffer.extend_from_slice(item.as_ref());
221                    let length = buffer.len() as u64;
222
223                    let mut reader = io::Cursor::new(buffer);
224                    let parser = IppParser::new(&mut reader);
225
226                    match parser.parse() {
227                        Ok(mut result) => {
228                            debug!("Parse ok, proceeding to payload state");
229                            if reader.position() < length {
230                                debug!("Adding residual payload from this chunk");
231                                let mut temp = tempfile::NamedTempFile::new()?;
232                                io::copy(&mut reader, &mut temp)?;
233                                result.payload = Some(PayloadKind::ReceivedData(temp));
234                            }
235                            self.state = AsyncParseState::Payload(result);
236                        }
237                        Err(ParseError::Incomplete) => {
238                            debug!("Incomplete request, awaiting for more data");
239                        }
240                        Err(e) => {
241                            return Err(e);
242                        }
243                    }
244                }
245                AsyncParseState::Payload(ref mut result) => {
246                    let mut reader = io::Cursor::new(&item);
247                    match result.payload {
248                        Some(PayloadKind::ReceivedData(ref mut file)) => {
249                            debug!(
250                                "Payload chunk received, appending to existing file: {}",
251                                file.path().display()
252                            );
253                            io::copy(&mut reader, file)?;
254                        }
255                        None => {
256                            let mut temp = tempfile::NamedTempFile::new()?;
257                            debug!("Payload chunk received, creating new file: {}", temp.path().display());
258                            io::copy(&mut reader, &mut temp)?;
259                            result.payload = Some(PayloadKind::ReceivedData(temp));
260                        }
261                        _ => panic!("Should not happen"),
262                    }
263                }
264            }
265        }
266
267        match self.state {
268            AsyncParseState::Headers(_) => Err(ParseError::Incomplete),
269            AsyncParseState::Payload(ref mut result) => {
270                debug!("Parsing finished, payload: {}", result.payload.is_some());
271                Ok(Async::Ready(IppParseResult {
272                    header: result.header.clone(),
273                    attributes: result.attributes.clone(),
274                    payload: result.payload.take(),
275                }))
276            }
277        }
278    }
279}
280
281impl<I, E> From<Box<dyn Stream<Item = I, Error = E> + Send>> for AsyncIppParser<I, E> {
282    /// Construct asynchronous parser from the stream
283    fn from(s: Box<dyn Stream<Item = I, Error = E> + Send>) -> AsyncIppParser<I, E> {
284        AsyncIppParser {
285            state: AsyncParseState::Headers(Vec::new()),
286            stream: s,
287        }
288    }
289}
290
291#[cfg(test)]
292mod tests {
293    use std::io::Cursor;
294
295    use super::*;
296
297    #[test]
298    fn test_parse_no_attributes() {
299        let data = &[1, 1, 0, 0, 0, 0, 0, 0, 3];
300        let result = IppParser::new(&mut Cursor::new(data)).parse();
301        assert!(result.is_ok());
302
303        let res = result.ok().unwrap();
304        assert!(res.attributes.groups().is_empty());
305    }
306
307    #[test]
308    fn test_parse_single_value() {
309        let data = &[
310            1, 1, 0, 0, 0, 0, 0, 0, 4, 0x21, 0x00, 0x04, b't', b'e', b's', b't', 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, 3,
311        ];
312        let result = IppParser::new(&mut Cursor::new(data)).parse();
313        assert!(result.is_ok());
314
315        let res = result.ok().unwrap();
316        let attrs = res.attributes.groups_of(DelimiterTag::PrinterAttributes)[0].attributes();
317        let attr = attrs.get("test").unwrap();
318        assert_eq!(attr.value().as_integer(), Some(&0x12345678));
319    }
320
321    #[test]
322    fn test_parse_list() {
323        let data = &[
324            1, 1, 0, 0, 0, 0, 0, 0, 4, 0x21, 0x00, 0x04, b't', b'e', b's', b't', 0x00, 0x04, 0x12, 0x34, 0x56, 0x78,
325            0x21, 0x00, 0x00, 0x00, 0x04, 0x77, 0x65, 0x43, 0x21, 3,
326        ];
327        let result = IppParser::new(&mut Cursor::new(data)).parse();
328        assert!(result.is_ok());
329
330        let res = result.ok().unwrap();
331        let attrs = res.attributes.groups_of(DelimiterTag::PrinterAttributes)[0].attributes();
332        let attr = attrs.get("test").unwrap();
333        assert_eq!(
334            attr.value().as_listof(),
335            Some(&vec![IppValue::Integer(0x12345678), IppValue::Integer(0x77654321)])
336        );
337    }
338
339    #[test]
340    fn test_parse_collection() {
341        let data = vec![
342            1, 1, 0, 0, 0, 0, 0, 0, 4, 0x34, 0, 4, b'c', b'o', b'l', b'l', 0, 0, 0x21, 0, 0, 0, 4, 0x12, 0x34, 0x56,
343            0x78, 0x44, 0, 0, 0, 3, b'k', b'e', b'y', 0x37, 0, 0, 0, 0, 3,
344        ];
345        let result = IppParser::new(&mut Cursor::new(data)).parse();
346        assert!(result.is_ok());
347
348        let res = result.ok().unwrap();
349        let attrs = res.attributes.groups_of(DelimiterTag::PrinterAttributes)[0].attributes();
350        let attr = attrs.get("coll").unwrap();
351        assert_eq!(
352            attr.value().as_collection(),
353            Some(&vec![
354                IppValue::Integer(0x12345678),
355                IppValue::Keyword("key".to_owned())
356            ])
357        );
358    }
359
360    #[test]
361    fn test_async_parser_with_payload() {
362        // split IPP into arbitrary chunks
363        let data = vec![
364            vec![1, 1, 0],
365            vec![0, 0, 0, 0, 0, 4],
366            vec![
367                0x21, 0x00, 0x04, b't', b'e', b's', b't', 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, 3,
368            ],
369            vec![b'f'],
370            vec![b'o', b'o'],
371        ];
372
373        let source: Box<Stream<Item = Vec<u8>, Error = io::Error> + Send> =
374            Box::new(futures::stream::iter_ok::<_, io::Error>(data));
375
376        let parser = AsyncIppParser::from(source);
377
378        let mut runtime = tokio::runtime::Runtime::new().unwrap();
379        let result = runtime.block_on(parser);
380        assert!(result.is_ok());
381
382        let res = result.ok().unwrap();
383        let attrs = res.attributes.groups_of(DelimiterTag::PrinterAttributes)[0].attributes();
384        let attr = attrs.get("test").unwrap();
385        assert_eq!(attr.value().as_integer(), Some(&0x12345678));
386
387        match res.payload {
388            Some(PayloadKind::ReceivedData(f)) => {
389                let foo = std::fs::read_to_string(f.path()).unwrap();
390                assert_eq!(foo, "foo");
391            }
392            _ => panic!("Wrong payload!"),
393        }
394    }
395
396}