SCLogDebug!("CONFIRMED WRITE: large record {}, file chunk xfer", rec_size);
// lets try to parse the RPC record. Might fail with Incomplete.
- match parse_rpc(cur_i) {
+ match parse_rpc(cur_i, cur_i.len(), false) {
Ok((remaining, ref hdr)) => {
match parse_nfs3_request_write(hdr.prog_data) {
Ok((_, ref w)) => {
}
// we have the full records size worth of data,
- // let's parse it
- match parse_rpc(&cur_i[..rec_size]) {
+ // let's parse it. Errors lead to event, but are
+ // not fatal as we already have enough info to
+ // go to the next record.
+ match parse_rpc(cur_i, cur_i.len(), true) {
Ok((_, ref rpc_record)) => {
- cur_i = &cur_i[rec_size..];
self.process_request_record(rpc_record);
- },
+ }
Err(nom::Err::Incomplete(_)) => {
- cur_i = &cur_i[rec_size..]; // progress input past parsed record
-
- // we shouldn't get incomplete as we have the full data
- // so if we got incomplete anyway it's the data that is
- // bad.
self.set_event(NFSEvent::MalformedData);
- },
+ }
Err(nom::Err::Error(_e)) |
Err(nom::Err::Failure(_e)) => {
self.set_event(NFSEvent::MalformedData);
SCLogDebug!("Parsing failed: {:?}", _e);
- return AppLayerResult::err();
- },
+ }
}
- },
+ cur_i = &cur_i[rec_size..];
+ }
Err(nom::Err::Incomplete(needed)) => {
if let nom::Needed::Size(n) = needed {
SCLogDebug!("Not enough data for partial RPC header {:?}", needed);
return AppLayerResult::incomplete((i.len() - cur_i.len()) as u32, need as u32);
}
return AppLayerResult::err();
- },
+ }
+ /* This error is fatal. If we failed to parse the RPC hdr we don't
+ * have a length and we don't know where the next record starts. */
Err(nom::Err::Error(_e)) |
Err(nom::Err::Failure(_e)) => {
self.set_event(NFSEvent::MalformedData);
SCLogDebug!("Parsing failed: {:?}", _e);
return AppLayerResult::err();
- },
+ }
}
};
SCLogDebug!("CONFIRMED large READ record {}, likely file chunk xfer", rec_size);
// we should have enough data to parse the RPC record
- match parse_rpc_reply(cur_i) {
+ match parse_rpc_reply(cur_i, cur_i.len(), false) {
Ok((remaining, ref hdr)) => {
match parse_nfs3_reply_read(hdr.prog_data) {
Ok((_, ref r)) => {
}
// we have the full data of the record, lets parse
- match parse_rpc_reply(&cur_i[..rec_size]) {
+ match parse_rpc_reply(cur_i, cur_i.len(), true) {
Ok((_, ref rpc_record)) => {
- cur_i = &cur_i[rec_size..]; // progress input past parsed record
self.process_reply_record(rpc_record);
- },
+ }
Err(nom::Err::Incomplete(_)) => {
- cur_i = &cur_i[rec_size..]; // progress input past parsed record
-
// we shouldn't get incomplete as we have the full data
// so if we got incomplete anyway it's the data that is
// bad.
self.set_event(NFSEvent::MalformedData);
- },
+ }
Err(nom::Err::Error(_e)) |
Err(nom::Err::Failure(_e)) => {
self.set_event(NFSEvent::MalformedData);
SCLogDebug!("Parsing failed: {:?}", _e);
- return AppLayerResult::err();
}
}
- },
+ cur_i = &cur_i[rec_size..]; // progress input past parsed record
+ }
Err(nom::Err::Incomplete(needed)) => {
if let nom::Needed::Size(n) = needed {
SCLogDebug!("Not enough data for partial RPC header {:?}", needed);
return AppLayerResult::incomplete((i.len() - cur_i.len()) as u32, need as u32);
}
return AppLayerResult::err();
- },
+ }
+ /* This error is fatal. If we failed to parse the RPC hdr we don't
+ * have a length and we don't know where the next record starts. */
Err(nom::Err::Error(_e)) |
Err(nom::Err::Failure(_e)) => {
self.set_event(NFSEvent::MalformedData);
SCLogDebug!("Parsing failed: {:?}", _e);
return AppLayerResult::err();
- },
+ }
}
};
self.post_gap_housekeeping(STREAM_TOCLIENT);
pub fn nfs_probe(i: &[u8], direction: u8) -> i8 {
if direction == STREAM_TOCLIENT {
- match parse_rpc_reply(i) {
+ match parse_rpc_reply(i, i.len(), false) {
Ok((_, ref rpc)) => {
if rpc.hdr.frag_len >= 24 && rpc.hdr.frag_len <= 35000 && rpc.hdr.msgtype == 1 && rpc.reply_state == 0 && rpc.accept_state == 0 {
SCLogDebug!("TC PROBE LEN {} XID {} TYPE {}", rpc.hdr.frag_len, rpc.hdr.xid, rpc.hdr.msgtype);
},
}
} else {
- match parse_rpc(i) {
+ match parse_rpc(i, i.len(), false) {
Ok((_, ref rpc)) => {
if rpc.hdr.frag_len >= 40 && rpc.hdr.msgtype == 0 &&
rpc.rpcver == 2 && (rpc.progver == 3 || rpc.progver == 4) &&
//! Nom parsers for RPCv2
use nom::IResult;
-use nom::combinator::rest;
+use nom::combinator::{rest, rest_len};
use nom::number::streaming::be_u32;
pub const RPC_MAX_MACHINE_SIZE: u32 = 256; // Linux kernel defines 64.
pub reply_state: u32,
pub accept_state: u32,
- pub prog_data: &'a[u8],
+ pub prog_data_size: u32,
+ pub prog_data: &'a [u8],
}
// top of request packet, just to get to procedure
pub verifier_len: u32,
pub verifier: &'a[u8],
+ pub prog_data_size: u32,
pub prog_data: &'a[u8],
}
-named!(pub parse_rpc<RpcPacket>,
+// Parse request RPC record.
+//
+// Can be called from 2 paths:
+// 1. we have all data -> do full parsing
+// 2. we have partial data (large records) -> allow partial prog_data parsing
+//
+// Arguments:
+// * `complete`:
+// type: bool
+// description: do full parsing, including of `prog_data`
+//
+named_args!(pub parse_rpc(start_i: usize, complete: bool) <RpcPacket>,
do_parse!(
hdr: parse_rpc_packet_header
-
>> rpcver: be_u32
>> program: be_u32
>> progver: be_u32
>> verifier_flavor: be_u32
>> verifier_len: verify!(be_u32, |&size| size < RPC_MAX_VERIFIER_SIZE)
>> verifier: take!(verifier_len as usize)
-
- >> pl: rest
-
+ >> rem_len: rest_len
+ >> prog_data_size: value!(hdr.frag_len + 4 - (start_i - rem_len) as u32)
+ >> prog_data_f: cond!(complete, take!(prog_data_size))
+ >> prog_data_t: cond!(!complete, rest)
>> (
RpcPacket {
hdr:hdr,
verifier_len:verifier_len,
verifier:verifier,
- prog_data:pl,
+ prog_data_size:prog_data_size as u32,
+ prog_data: if complete { prog_data_f.unwrap() } else { prog_data_t.unwrap() },
}
))
);
-// to be called with data <= hdr.frag_len + 4. Sending more data is undefined.
-named!(pub parse_rpc_reply<RpcReplyPacket>,
+// Parse reply RPC record.
+//
+// Can be called from 2 paths:
+// 1. we have all data -> do full parsing
+// 2. we have partial data (large records) -> allow partial prog_data parsing
+//
+// Arguments:
+// * `complete`:
+// type: bool
+// description: do full parsing, including of `prog_data`
+//
+named_args!(pub parse_rpc_reply(start_i: usize, complete: bool) <RpcReplyPacket>,
do_parse!(
hdr: parse_rpc_packet_header
>> verifier: cond!(verifier_len > 0, take!(verifier_len as usize))
>> accept_state: be_u32
-
- >> pl: rest
+ >> rem_len: rest_len
+ >> prog_data_size: value!(hdr.frag_len + 4 - (start_i - rem_len) as u32)
+ >> prog_data_f: cond!(complete, take!(prog_data_size))
+ >> prog_data_t: cond!(!complete, rest)
>> (
RpcReplyPacket {
reply_state:reply_state,
accept_state:accept_state,
- prog_data:pl,
+ prog_data_size:prog_data_size as u32,
+ prog_data: if complete { prog_data_f.unwrap() } else { prog_data_t.unwrap() },
}
))
);
verifier_flavor:verifier_flavor,
verifier_len:verifier_len,
verifier:verifier,
-
+ prog_data_size:pl.len() as u32,
prog_data:pl,
}
))
reply_state:reply_state,
accept_state:accept_state,
-
- prog_data:pl,
+ prog_data_size:pl.len() as u32,
+ prog_data:pl
}
))
);