-/* Copyright (C) 2017-2021 Open Information Security Foundation
+/* Copyright (C) 2017-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
// TODO maybe not enough users to justify a func
pub fn mark_response_tx_done(
- &mut self, xid: u32, rpc_status: u32, nfs_status: u32, resp_handle: &[u8],
+ &mut self, flow: *const Flow, xid: u32, rpc_status: u32, nfs_status: u32, resp_handle: &[u8],
) {
if let Some(mytx) = self.get_tx_by_xid(xid) {
mytx.tx_data.updated_tc = true;
mytx.request_done,
mytx.response_done
);
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToClient as i32);
} else {
//SCLogNotice!("process_reply_record: not TX found for XID {}", r.hdr.xid);
}
}
3 => {
self.add_nfs_ts_frame(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
- self.process_request_record_v3(r)
+ self.process_request_record_v3(flow, r)
}
2 => {
self.add_nfs_ts_frame(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
return None;
}
- pub fn process_write_record<'b>(&mut self, r: &RpcPacket<'b>, w: &Nfs3RequestWrite<'b>) -> u32 {
+ pub fn process_write_record<'b>(&mut self, flow: *const Flow, r: &RpcPacket<'b>, w: &Nfs3RequestWrite<'b>) -> u32 {
let mut fill_bytes = 0;
let pad = w.count % 4;
if pad != 0 {
tx.is_last = true;
tx.response_done = true;
tx.is_file_closed = true;
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToClient as i32);
}
true
} else {
tx.is_last = true;
tx.request_done = true;
tx.is_file_closed = true;
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToServer as i32);
}
}
}
}
fn process_partial_write_request_record<'b>(
- &mut self, r: &RpcPacket<'b>, w: &Nfs3RequestWrite<'b>,
+ &mut self, flow: *const Flow, r: &RpcPacket<'b>, w: &Nfs3RequestWrite<'b>,
) -> u32 {
SCLogDebug!(
"REQUEST {} procedure {} blob size {}",
xidmap.file_handle = w.handle.value.to_vec();
self.requestmap.insert(r.hdr.xid, xidmap);
- return self.process_write_record(r, w);
+ return self.process_write_record(flow, r, w);
}
fn process_reply_record(
2 => {
SCLogDebug!("NFSv2 reply record");
self.add_nfs_tc_frames(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
- self.process_reply_record_v2(r, &xidmap);
+ self.process_reply_record_v2(flow, r, &xidmap);
return 0;
}
3 => {
SCLogDebug!("NFSv3 reply record");
self.add_nfs_tc_frames(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
- self.process_reply_record_v3(r, &mut xidmap);
+ self.process_reply_record_v3(flow, r, &mut xidmap);
return 0;
}
4 => {
SCLogDebug!("NFSv4 reply record");
self.add_nfs4_tc_frames(flow, stream_slice, r.prog_data, r.prog_data_size as i64);
- self.process_reply_record_v4(r, &mut xidmap);
+ self.process_reply_record_v4(flow, r, &mut xidmap);
return 0;
}
_ => {
// update in progress chunks for file transfers
// return how much data we consumed
- fn filetracker_update(&mut self, direction: Direction, data: &[u8], gap_size: u32) -> u32 {
+ fn filetracker_update(&mut self, flow: *const Flow, direction: Direction, data: &[u8], gap_size: u32) -> u32 {
let mut chunk_left = if direction == Direction::ToServer {
self.ts_chunk_left
} else {
"TX {} response is done now that the file track is ready",
tx.id
);
+ if !flow.is_null() {
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToClient as i32);
+ }
} else {
tx.request_done = true;
tx.is_file_closed = true;
"TX {} request is done now that the file track is ready",
tx.id
);
+ if !flow.is_null() {
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToServer as i32);
+ }
}
}
cs
/// xidmapr is an Option as it's already removed from the map if we
/// have a complete record. Otherwise we do a lookup ourselves.
pub fn process_read_record<'b>(
- &mut self, r: &RpcReplyPacket<'b>, reply: &NfsReplyRead<'b>,
+ &mut self, flow: *const Flow, r: &RpcReplyPacket<'b>, reply: &NfsReplyRead<'b>,
xidmapr: Option<&NFSRequestXidMap>,
) -> u32 {
let file_name;
tx.nfs_response_status = reply.status;
tx.is_last = true;
tx.request_done = true;
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToServer as i32);
/* if this is a partial record we will close the tx
* when we've received the final data */
if !is_partial {
tx.response_done = true;
SCLogDebug!("TX {} is DONE", tx.id);
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToClient as i32);
}
}
true
tx.nfs_response_status = reply.status;
tx.is_last = true;
tx.request_done = true;
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToServer as i32);
/* if this is a partial record we will close the tx
* when we've received the final data */
if !is_partial {
tx.response_done = true;
SCLogDebug!("TX {} is DONE", tx.id);
+ sc_app_layer_parser_trigger_raw_stream_inspection(flow, Direction::ToClient as i32);
}
}
}
}
fn process_partial_read_reply_record<'b>(
- &mut self, r: &RpcReplyPacket<'b>, reply: &NfsReplyRead<'b>,
+ &mut self, flow: *const Flow, r: &RpcReplyPacket<'b>, reply: &NfsReplyRead<'b>,
) -> u32 {
SCLogDebug!(
"REPLY {} to procedure READ blob size {} / {}",
reply.count
);
- return self.process_read_record(r, reply, None);
+ return self.process_read_record(flow, r, reply, None);
}
fn peek_reply_record(&mut self, r: &RpcPacketHeader) -> u32 {
pub fn parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> AppLayerResult {
SCLogDebug!("parse_tcp_data_ts_gap ({})", gap_size);
let gap = vec![0; gap_size as usize];
- let consumed = self.filetracker_update(Direction::ToServer, &gap, gap_size);
+ let consumed = self.filetracker_update(std::ptr::null(), Direction::ToServer, &gap, gap_size);
if consumed > gap_size {
SCLogDebug!("consumed more than GAP size: {} > {}", consumed, gap_size);
return AppLayerResult::ok();
pub fn parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> AppLayerResult {
SCLogDebug!("parse_tcp_data_tc_gap ({})", gap_size);
let gap = vec![0; gap_size as usize];
- let consumed = self.filetracker_update(Direction::ToClient, &gap, gap_size);
+ let consumed = self.filetracker_update(std::ptr::null(), Direction::ToClient, &gap, gap_size);
if consumed > gap_size {
SCLogDebug!("consumed more than GAP size: {} > {}", consumed, gap_size);
return AppLayerResult::ok();
/// Handle partial records
fn parse_tcp_partial_data_ts<'b>(
- &mut self, base_input: &'b [u8], cur_i: &'b [u8], phdr: &RpcRequestPacketPartial,
+ &mut self, flow: *const Flow, base_input: &'b [u8], cur_i: &'b [u8], phdr: &RpcRequestPacketPartial,
rec_size: usize,
) -> AppLayerResult {
// special case: avoid buffering file write blobs
match parse_nfs3_request_write(hdr.prog_data, false) {
Ok((_, ref w)) => {
// deal with the partial nfs write data
- self.process_partial_write_request_record(hdr, w);
+ self.process_partial_write_request_record(flow, hdr, w);
return AppLayerResult::ok();
}
Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
let mut cur_i = stream_slice.as_slice();
// take care of in progress file chunk transfers
// and skip buffer beyond it
- let consumed = self.filetracker_update(Direction::ToServer, cur_i, 0);
+ let consumed = self.filetracker_update(flow, Direction::ToServer, cur_i, 0);
if consumed > 0 {
if consumed > cur_i.len() as u32 {
return AppLayerResult::err();
// Handle partial records
if rec_size > cur_i.len() {
return self.parse_tcp_partial_data_ts(
+ flow,
stream_slice.as_slice(),
cur_i,
rpc_phdr,
/// Handle partial records
fn parse_tcp_partial_data_tc<'b>(
- &mut self, base_input: &'b [u8], cur_i: &'b [u8], phdr: &RpcPacketHeader, rec_size: usize,
+ &mut self, flow: *const Flow, base_input: &'b [u8], cur_i: &'b [u8], phdr: &RpcPacketHeader, rec_size: usize,
) -> AppLayerResult {
// special case: avoid buffering file read blobs
// as these can be large.
match parse_nfs3_reply_read(hdr.prog_data, false) {
Ok((_, ref r)) => {
// deal with the partial nfs read data
- self.process_partial_read_reply_record(hdr, r);
+ self.process_partial_read_reply_record(flow, hdr, r);
return AppLayerResult::ok();
}
Err(Err::Error(_e)) | Err(Err::Failure(_e)) => {
let mut cur_i = stream_slice.as_slice();
// take care of in progress file chunk transfers
// and skip buffer beyond it
- let consumed = self.filetracker_update(Direction::ToClient, cur_i, 0);
+ let consumed = self.filetracker_update(flow, Direction::ToClient, cur_i, 0);
if consumed > 0 {
if consumed > cur_i.len() as u32 {
return AppLayerResult::err();
// see if we have all data available
if rec_size > cur_i.len() {
return self.parse_tcp_partial_data_tc(
+ flow,
stream_slice.as_slice(),
cur_i,
rpc_phdr,
use crate::direction::Direction;
use crate::nfs::nfs::*;
+use crate::flow::Flow;
use crate::nfs::nfs4_records::*;
use crate::nfs::nfs_records::*;
use crate::nfs::rpc_records::*;
}
fn compound_response<'b>(
- &mut self, r: &RpcReplyPacket<'b>, cr: &Nfs4ResponseCompoundRecord<'b>,
+ &mut self, flow: *const Flow, r: &RpcReplyPacket<'b>, cr: &Nfs4ResponseCompoundRecord<'b>,
xidmap: &mut NFSRequestXidMap,
) {
let mut insert_filename_with_getfh = false;
data_len: rd.data.len() as u32,
data: rd.data,
};
- self.process_read_record(r, &reply, Some(xidmap));
+ self.process_read_record(flow, r, &reply, Some(xidmap));
}
Nfs4ResponseContent::Open(_s, Some(ref _rd)) => {
SCLogDebug!("OPENv4: status {} opendata {:?}", _s, _rd);
if main_opcode_status_set {
let resp_handle = Vec::new();
- self.mark_response_tx_done(r.hdr.xid, r.reply_state, main_opcode_status, &resp_handle);
+ self.mark_response_tx_done(flow, r.hdr.xid, r.reply_state, main_opcode_status, &resp_handle);
}
}
- pub fn process_reply_record_v4(&mut self, r: &RpcReplyPacket, xidmap: &mut NFSRequestXidMap) {
+ pub fn process_reply_record_v4(&mut self, flow: *const Flow, r: &RpcReplyPacket, xidmap: &mut NFSRequestXidMap) {
if xidmap.procedure == NFSPROC4_COMPOUND {
let mut data = r.prog_data;
match parse_nfs4_response_compound(data) {
Ok((_, rd)) => {
SCLogDebug!("COMPOUNDv4: {:?}", rd);
- self.compound_response(r, &rd, xidmap);
+ self.compound_response(flow, r, &rd, xidmap);
}
Err(Err::Incomplete(_)) => {
self.set_event(NFSEvent::MalformedData);