ts_gap: bool, // last TS update was gap
tc_gap: bool, // last TC update was gap
+ is_udp: bool,
+
/// tx counter for assigning incrementing id's to tx's
tx_id: u64,
tc_ssn_gap:false,
ts_gap:false,
tc_gap:false,
+ is_udp:false,
tx_id:0,
de_state_count:0,
//ts_txs_updated:false,
tx.request_done = true;
}
}
- self.ts_chunk_xid = r.hdr.xid;
- let file_data_len = w.file_data.len() as u32 - fill_bytes as u32;
- self.ts_chunk_left = w.file_len as u32 - file_data_len as u32;
+ if !self.is_udp {
+ self.ts_chunk_xid = r.hdr.xid;
+ let file_data_len = w.file_data.len() as u32 - fill_bytes as u32;
+ self.ts_chunk_left = w.file_len as u32 - file_data_len as u32;
+ }
0
}
//if is_last {
// self.tc_txs_updated = true;
//}
- self.tc_chunk_xid = r.hdr.xid;
- self.tc_chunk_left = reply.count as u32 - reply.data.len() as u32;
+ if !self.is_udp {
+ self.tc_chunk_xid = r.hdr.xid;
+ self.tc_chunk_left = reply.count as u32 - reply.data.len() as u32;
+ }
SCLogDebug!("REPLY {} to procedure {} blob size {} / {}: chunk_left {}",
r.hdr.xid, NFSPROC3_READ, r.prog_data.len(), reply.count, self.tc_chunk_left);
};
status
}
+ /// Parsing function
+ pub fn parse_udp_ts<'b>(&mut self, input: &'b[u8]) -> u32 {
+ let mut status = 0;
+ SCLogDebug!("parse_udp_ts ({})", input.len());
+ if input.len() > 0 {
+ match parse_rpc_udp_request(input) {
+ IResult::Done(_, ref rpc_record) => {
+ self.is_udp = true;
+ status |= self.process_request_record(rpc_record);
+ },
+ IResult::Incomplete(_) => {
+ },
+ IResult::Error(e) => { panic!("Parsing failed: {:?}",e); //break
+ },
+ }
+ }
+ status
+ }
+
+ /// Parsing function
+ pub fn parse_udp_tc<'b>(&mut self, input: &'b[u8]) -> u32 {
+ let mut status = 0;
+ SCLogDebug!("parse_udp_tc ({})", input.len());
+ if input.len() > 0 {
+ match parse_rpc_udp_reply(input) {
+ IResult::Done(_, ref rpc_record) => {
+ self.is_udp = true;
+ status |= self.process_reply_record(rpc_record);
+ },
+ IResult::Incomplete(_) => {
+ },
+ IResult::Error(e) => { panic!("Parsing failed: {:?}",e); //break
+ },
+ }
+ };
+ status
+ }
fn getfiles(&mut self, direction: u8) -> * mut FileContainer {
//SCLogDebug!("direction: {}", direction);
if direction == STREAM_TOCLIENT {
}
}
+/// C binding parse a DNS request. Returns 1 on success, -1 on failure.
+#[no_mangle]
+pub extern "C" fn rs_nfs3_parse_request_udp(_flow: *mut Flow,
+ state: &mut NFS3State,
+ _pstate: *mut libc::c_void,
+ input: *mut libc::uint8_t,
+ input_len: libc::uint32_t,
+ _data: *mut libc::c_void)
+ -> libc::int8_t
+{
+ let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
+ SCLogDebug!("parsing {} bytes of request data", input_len);
+
+ if state.parse_udp_ts(buf) == 0 {
+ 1
+ } else {
+ -1
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_nfs3_parse_response_udp(_flow: *mut Flow,
+ state: &mut NFS3State,
+ _pstate: *mut libc::c_void,
+ input: *mut libc::uint8_t,
+ input_len: libc::uint32_t,
+ _data: *mut libc::c_void)
+ -> libc::int8_t
+{
+ SCLogDebug!("parsing {} bytes of response data", input_len);
+ let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
+
+ if state.parse_udp_tc(buf) == 0 {
+ 1
+ } else {
+ -1
+ }
+}
+
#[no_mangle]
pub extern "C" fn rs_nfs3_state_get_tx_count(state: &mut NFS3State)
-> libc::uint64_t
}
}
+pub fn nfs3_probe_udp(i: &[u8], direction: u8) -> i8 {
+ if direction == STREAM_TOCLIENT {
+ match parse_rpc_udp_reply(i) {
+ IResult::Done(_, ref rpc) => {
+ if i.len() >= 32 && rpc.hdr.msgtype == 1 && rpc.reply_state == 0 && rpc.accept_state == 0 {
+ SCLogNotice!("TC PROBE LEN {} XID {} TYPE {}", rpc.hdr.frag_len, rpc.hdr.xid, rpc.hdr.msgtype);
+ return 1;
+ } else {
+ return -1;
+ }
+ },
+ IResult::Incomplete(_) => {
+ return -1;
+ },
+ IResult::Error(_) => {
+ return -1;
+ },
+ }
+ } else {
+ match parse_rpc_udp_request(i) {
+ IResult::Done(_, ref rpc) => {
+ if i.len() >= 48 && rpc.hdr.msgtype == 0 && rpc.progver == 3 && rpc.program == 100003 {
+ return 1;
+ } else {
+ return -1;
+ }
+ },
+ IResult::Incomplete(_) => {
+ return -1;
+ },
+ IResult::Error(_) => {
+ return -1;
+ },
+ }
+ }
+}
+
/// TOSERVER probe function
#[no_mangle]
pub extern "C" fn rs_nfs_probe_ts(input: *const libc::uint8_t, len: libc::uint32_t)
return nfs3_probe(slice, STREAM_TOCLIENT);
}
+/// TOSERVER probe function
+#[no_mangle]
+pub extern "C" fn rs_nfs_probe_udp_ts(input: *const libc::uint8_t, len: libc::uint32_t)
+ -> libc::int8_t
+{
+ let slice: &[u8] = unsafe {
+ std::slice::from_raw_parts(input as *mut u8, len as usize)
+ };
+ return nfs3_probe_udp(slice, STREAM_TOSERVER);
+}
+/// TOCLIENT probe function
+#[no_mangle]
+pub extern "C" fn rs_nfs_probe_udp_tc(input: *const libc::uint8_t, len: libc::uint32_t)
+ -> libc::int8_t
+{
+ let slice: &[u8] = unsafe {
+ std::slice::from_raw_parts(input as *mut u8, len as usize)
+ };
+ return nfs3_probe_udp(slice, STREAM_TOCLIENT);
+}
#[no_mangle]
pub extern "C" fn rs_nfs3_getfiles(direction: u8, ptr: *mut NFS3State) -> * mut FileContainer {
--- /dev/null
+/* Copyright (C) 2015 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
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * NFS3 application layer detector and parser for learning and
+ * nfs3 pruposes.
+ *
+ * This nfs3 implements a simple application layer for something
+ * like the NFS3 protocol running on port 2049.
+ */
+
+#include "suricata-common.h"
+#include "stream.h"
+#include "conf.h"
+
+#include "util-unittest.h"
+
+#include "app-layer-detect-proto.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-nfs3-udp.h"
+
+#ifndef HAVE_RUST
+void RegisterNFS3UDPParsers(void)
+{
+}
+
+#else
+
+#include "rust.h"
+#include "rust-nfs-nfs3-gen.h"
+
+/* The default port to probe for echo traffic if not provided in the
+ * configuration file. */
+#define NFS3_DEFAULT_PORT "2049"
+
+/* The minimum size for a RFC message. For some protocols this might
+ * be the size of a header. TODO actual min size is likely larger */
+#define NFS3_MIN_FRAME_LEN 32
+
+/* Enum of app-layer events for an echo protocol. Normally you might
+ * have events for errors in parsing data, like unexpected data being
+ * received. For echo we'll make something up, and log an app-layer
+ * level alert if an empty message is received.
+ *
+ * Example rule:
+ *
+ * alert nfs3 any any -> any any (msg:"SURICATA NFS3 empty message"; \
+ * app-layer-event:nfs3.empty_message; sid:X; rev:Y;)
+ */
+enum {
+ NFS3_DECODER_EVENT_EMPTY_MESSAGE,
+};
+
+SCEnumCharMap nfs3_udp_decoder_event_table[] = {
+ {"EMPTY_MESSAGE", NFS3_DECODER_EVENT_EMPTY_MESSAGE},
+ { NULL, 0 }
+};
+
+static void *NFS3StateAlloc(void)
+{
+ return rs_nfs3_state_new();
+}
+
+static void NFS3StateFree(void *state)
+{
+ rs_nfs3_state_free(state);
+}
+
+/**
+ * \brief Callback from the application layer to have a transaction freed.
+ *
+ * \param state a void pointer to the NFS3State object.
+ * \param tx_id the transaction ID to free.
+ */
+static void NFS3StateTxFree(void *state, uint64_t tx_id)
+{
+ rs_nfs3_state_tx_free(state, tx_id);
+}
+
+#if 0
+static int NFS3StateGetEventInfo(const char *event_name, int *event_id,
+ AppLayerEventType *event_type)
+{
+ *event_id = SCMapEnumNameToValue(event_name, nfs3_decoder_event_table);
+ if (*event_id == -1) {
+ SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
+ "nfs3 enum map table.", event_name);
+ /* This should be treated as fatal. */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
+
+ return 0;
+}
+
+static AppLayerDecoderEvents *NFS3GetEvents(void *state, uint64_t tx_id)
+{
+ NFS3State *nfs3_state = state;
+ NFS3Transaction *tx;
+
+ TAILQ_FOREACH(tx, &nfs3_state->tx_list, next) {
+ if (tx->tx_id == tx_id) {
+ return tx->decoder_events;
+ }
+ }
+
+ return NULL;
+}
+
+static int NFS3HasEvents(void *state)
+{
+ NFS3State *echo = state;
+ return echo->events;
+}
+#endif
+
+/**
+ * \brief Probe the input to see if it looks like echo.
+ *
+ * \retval ALPROTO_NFS3 if it looks like echo, otherwise
+ * ALPROTO_UNKNOWN.
+ */
+static AppProto NFS3ProbingParserTS(uint8_t *input, uint32_t input_len,
+ uint32_t *offset)
+{
+ SCLogNotice("probing");
+ if (input_len < NFS3_MIN_FRAME_LEN) {
+ SCLogNotice("unknown");
+ return ALPROTO_UNKNOWN;
+ }
+
+ int8_t r = rs_nfs_probe_udp_ts(input, input_len);
+ if (r == 1) {
+ SCLogNotice("nfs3");
+ return ALPROTO_NFS3;
+ } else if (r == -1) {
+ SCLogNotice("failed");
+ return ALPROTO_FAILED;
+ }
+
+ SCLogNotice("Protocol not detected as ALPROTO_NFS3.");
+ return ALPROTO_UNKNOWN;
+}
+
+static AppProto NFS3ProbingParserTC(uint8_t *input, uint32_t input_len,
+ uint32_t *offset)
+{
+ SCLogNotice("probing");
+ if (input_len < NFS3_MIN_FRAME_LEN) {
+ SCLogNotice("unknown");
+ return ALPROTO_UNKNOWN;
+ }
+
+ int8_t r = rs_nfs_probe_tc(input, input_len);
+ if (r == 1) {
+ SCLogNotice("nfs3");
+ return ALPROTO_NFS3;
+ } else if (r == -1) {
+ SCLogNotice("failed");
+ return ALPROTO_FAILED;
+ }
+
+ SCLogNotice("Protocol not detected as ALPROTO_NFS3.");
+ return ALPROTO_UNKNOWN;
+}
+
+static int NFS3ParseRequest(Flow *f, void *state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ uint16_t file_flags = FileFlowToFlags(f, STREAM_TOSERVER);
+ rs_nfs3_setfileflags(0, state, file_flags);
+
+ return rs_nfs3_parse_request_udp(f, state, pstate, input, input_len, local_data);
+}
+
+static int NFS3ParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len, void *local_data)
+{
+ uint16_t file_flags = FileFlowToFlags(f, STREAM_TOCLIENT);
+ rs_nfs3_setfileflags(1, state, file_flags);
+
+ return rs_nfs3_parse_response_udp(f, state, pstate, input, input_len, local_data);
+}
+
+static uint64_t NFS3GetTxCnt(void *state)
+{
+ return rs_nfs3_state_get_tx_count(state);
+}
+
+static void *NFS3GetTx(void *state, uint64_t tx_id)
+{
+ return rs_nfs3_state_get_tx(state, tx_id);
+}
+
+static void NFS3SetTxLogged(void *state, void *vtx, uint32_t logger)
+{
+ rs_nfs3_tx_set_logged(state, vtx, logger);
+}
+
+static int NFS3GetTxLogged(void *state, void *vtx, uint32_t logger)
+{
+ return rs_nfs3_tx_get_logged(state, vtx, logger);
+}
+
+/**
+ * \brief Called by the application layer.
+ *
+ * In most cases 1 can be returned here.
+ */
+static int NFS3GetAlstateProgressCompletionStatus(uint8_t direction) {
+ return rs_nfs3_state_progress_completion_status(direction);
+}
+
+/**
+ * \brief Return the state of a transaction in a given direction.
+ *
+ * In the case of the echo protocol, the existence of a transaction
+ * means that the request is done. However, some protocols that may
+ * need multiple chunks of data to complete the request may need more
+ * than just the existence of a transaction for the request to be
+ * considered complete.
+ *
+ * For the response to be considered done, the response for a request
+ * needs to be seen. The response_done flag is set on response for
+ * checking here.
+ */
+static int NFS3GetStateProgress(void *tx, uint8_t direction)
+{
+ return rs_nfs3_tx_get_alstate_progress(tx, direction);
+}
+
+/**
+ * \brief get stored tx detect state
+ */
+static DetectEngineState *NFS3GetTxDetectState(void *vtx)
+{
+ return rs_nfs3_state_get_tx_detect_state(vtx);
+}
+
+/**
+ * \brief set store tx detect state
+ */
+static int NFS3SetTxDetectState(void *state, void *vtx,
+ DetectEngineState *s)
+{
+ rs_nfs3_state_set_tx_detect_state(state, vtx, s);
+ return 0;
+}
+
+static FileContainer *NFS3GetFiles(void *state, uint8_t direction)
+{
+ return rs_nfs3_getfiles(direction, state);
+}
+
+static StreamingBufferConfig sbcfg = STREAMING_BUFFER_CONFIG_INITIALIZER;
+static SuricataFileContext sfc = { &sbcfg };
+
+void RegisterNFS3UDPParsers(void)
+{
+ const char *proto_name = "nfs3";
+
+ /* Check if NFS3 TCP detection is enabled. If it does not exist in
+ * the configuration file then it will be enabled by default. */
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
+
+ rs_nfs3_init(&sfc);
+
+ SCLogNotice("NFS3 UDP protocol detection enabled.");
+
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_NFS3, proto_name);
+
+ if (RunmodeIsUnittests()) {
+
+ SCLogNotice("Unittest mode, registering default configuration.");
+ AppLayerProtoDetectPPRegister(IPPROTO_UDP, NFS3_DEFAULT_PORT,
+ ALPROTO_NFS3, 0, NFS3_MIN_FRAME_LEN, STREAM_TOSERVER,
+ NFS3ProbingParserTS, NFS3ProbingParserTC);
+
+ }
+ else {
+
+ if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
+ proto_name, ALPROTO_NFS3, 0, NFS3_MIN_FRAME_LEN,
+ NFS3ProbingParserTS, NFS3ProbingParserTC)) {
+ SCLogNotice("No NFS3 app-layer configuration, enabling NFS3"
+ " detection TCP detection on port %s.",
+ NFS3_DEFAULT_PORT);
+ AppLayerProtoDetectPPRegister(IPPROTO_UDP,
+ NFS3_DEFAULT_PORT, ALPROTO_NFS3, 0,
+ NFS3_MIN_FRAME_LEN, STREAM_TOSERVER,
+ NFS3ProbingParserTS, NFS3ProbingParserTC);
+ }
+
+ }
+
+ }
+
+ else {
+ SCLogNotice("Protocol detecter and parser disabled for NFS3.");
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("udp", proto_name))
+ {
+ SCLogNotice("Registering NFS3 protocol parser.");
+
+ /* Register functions for state allocation and freeing. A
+ * state is allocated for every new NFS3 flow. */
+ AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_NFS3,
+ NFS3StateAlloc, NFS3StateFree);
+
+ /* Register request parser for parsing frame from server to client. */
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_NFS3,
+ STREAM_TOSERVER, NFS3ParseRequest);
+
+ /* Register response parser for parsing frames from server to client. */
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_NFS3,
+ STREAM_TOCLIENT, NFS3ParseResponse);
+
+ /* Register a function to be called by the application layer
+ * when a transaction is to be freed. */
+ AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_NFS3,
+ NFS3StateTxFree);
+
+ AppLayerParserRegisterLoggerFuncs(IPPROTO_UDP, ALPROTO_NFS3,
+ NFS3GetTxLogged, NFS3SetTxLogged);
+
+ /* Register a function to return the current transaction count. */
+ AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_NFS3,
+ NFS3GetTxCnt);
+
+ /* Transaction handling. */
+ AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_NFS3,
+ NFS3GetAlstateProgressCompletionStatus);
+ AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP,
+ ALPROTO_NFS3, NFS3GetStateProgress);
+ AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_NFS3,
+ NFS3GetTx);
+
+ AppLayerParserRegisterGetFilesFunc(IPPROTO_UDP, ALPROTO_NFS3, NFS3GetFiles);
+
+ /* Application layer event handling. */
+// AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_NFS3,
+// NFS3HasEvents);
+
+ /* What is this being registered for? */
+ AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_NFS3,
+ NULL, NFS3GetTxDetectState, NFS3SetTxDetectState);
+
+// AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_NFS3,
+// NFS3StateGetEventInfo);
+// AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_NFS3,
+// NFS3GetEvents);
+ }
+ else {
+ SCLogNotice("NFS3 protocol parsing disabled.");
+ }
+
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_NFS3,
+ NFS3UDPParserRegisterTests);
+#endif
+}
+
+#ifdef UNITTESTS
+#endif
+
+void NFS3UDPParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+#endif
+}
+
+#endif /* HAVE_RUST */