TFTP parsing and logging written in Rust.
Log on eve.json the type of request (read or write), the name of the file and
the mode.
Example of output:
"tftp":{"packet":"read","file":"rfc1350.txt","mode":"octet"}
"NFSTransaction": "NFSTransaction",
"NTPState": "NTPState",
"NTPTransaction": "NTPTransaction",
+ "TFTPTransaction": "TFTPTransaction",
+ "TFTPState": "TFTPState",
"JsonT": "json_t",
"DetectEngineState": "DetectEngineState",
"core::DetectEngineState": "DetectEngineState",
#[cfg(feature = "experimental")]
pub mod ntp;
+pub mod tftp;
--- /dev/null
+/* Copyright (C) 2017 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.
+ */
+
+// written by Clément Galland <clement.galland@epita.fr>
+
+extern crate libc;
+
+use json::*;
+use tftp::tftp::*;
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_log_json_request(tx: &mut TFTPTransaction) -> *mut JsonT
+{
+ let js = Json::object();
+ match tx.opcode {
+ 1 => js.set_string("packet", "read"),
+ 2 => js.set_string("packet", "write"),
+ _ => js.set_string("packet", "error")
+ };
+ js.set_string("file", tx.filename.as_str());
+ js.set_string("mode", tx.mode.as_str());
+ js.unwrap()
+}
--- /dev/null
+/* Copyright (C) 2017 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.
+ */
+
+// written by Clément Galland <clement.galland@epita.fr>
+
+pub mod tftp;
+pub mod log;
--- /dev/null
+/* Copyright (C) 2017 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.
+ */
+
+// written by Clément Galland <clement.galland@epita.fr>
+
+extern crate libc;
+extern crate nom;
+
+use std::str;
+use std;
+use std::mem::transmute;
+
+use applayer::LoggerFlags;
+
+#[derive(Debug)]
+pub struct TFTPTransaction {
+ pub opcode : u8,
+ pub filename : String,
+ pub mode : String,
+ pub logged : LoggerFlags
+}
+
+pub struct TFTPState {
+ pub transactions : Vec<TFTPTransaction>
+}
+
+impl TFTPTransaction {
+ pub fn new(opcode : u8, filename : String, mode : String) -> TFTPTransaction {
+ TFTPTransaction {
+ opcode : opcode,
+ filename : filename,
+ mode : mode.to_lowercase(),
+ logged : LoggerFlags::new(),
+ }
+ }
+ pub fn is_mode_ok(&self) -> bool {
+ match self.mode.as_str() {
+ "netascii" | "mail" | "octet" => true,
+ _ => false
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_state_alloc() -> *mut libc::c_void {
+ let state = TFTPState { transactions : Vec::new() };
+ let boxed = Box::new(state);
+ return unsafe{transmute(boxed)};
+}
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_state_free(state: *mut libc::c_void) {
+ let _state : Box<TFTPState> = unsafe{transmute(state)};
+}
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_state_tx_free(state: &mut TFTPState,
+ tx_id: libc::uint32_t) {
+ state.transactions.remove(tx_id as usize);
+}
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_get_tx(state: &mut TFTPState,
+ tx_id: libc::uint64_t) -> *mut libc::c_void {
+ if state.transactions.len() <= tx_id as usize {
+ return std::ptr::null_mut();
+ }
+ return unsafe{transmute(&state.transactions[tx_id as usize])};
+}
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_get_tx_logged(_state: &mut TFTPState,
+ tx: &mut TFTPTransaction,
+ logger: libc::uint32_t) -> i8 {
+ if tx.logged.is_logged(logger) {
+ 1
+ } else {
+ 0
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_set_tx_logged(_state: &mut TFTPState,
+ tx: &mut TFTPTransaction,
+ logger: libc::uint32_t) {
+ tx.logged.set_logged(logger);
+}
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_has_event(state: &mut TFTPState) -> i64 {
+ return state.transactions.len() as i64;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_get_tx_cnt(state: &mut TFTPState) -> u64 {
+ return state.transactions.len() as u64;
+}
+
+named!(getstr<&str>, map_res!(
+ take_while!(call!(|c| c != 0)),
+ str::from_utf8
+ )
+);
+
+named!(pub tftp_request<TFTPTransaction>,
+ do_parse!(
+ tag!([0]) >>
+ opcode: take!(1) >>
+ filename: getstr >>
+ tag!([0]) >>
+ mode : getstr >>
+ (
+ TFTPTransaction::new(opcode[0], String::from(filename), String::from(mode))
+ )
+ )
+);
+
+
+#[no_mangle]
+pub extern "C" fn rs_tftp_request(state: &mut TFTPState,
+ input: *const libc::uint8_t,
+ len: libc::uint32_t) -> i64 {
+ let buf = unsafe{std::slice::from_raw_parts(input, len as usize)};
+ return match tftp_request(buf) {
+ nom::IResult::Done(_, rqst) => {
+ state.transactions.push(rqst);
+ 1
+ },
+ _ => 0
+ }
+}
app-layer-nfs-udp.c app-layer-nfs-udp.h \
app-layer-ntp.c app-layer-ntp.h \
app-layer-register.c app-layer-register.h \
+app-layer-tftp.c app-layer-tftp.h \
app-layer-template.c app-layer-template.h \
app-layer-ssh.c app-layer-ssh.h \
app-layer-ssl.c app-layer-ssl.h \
output-json-stats.c output-json-stats.h \
output-json-tls.c output-json-tls.h \
output-json-nfs.c output-json-nfs.h \
+output-json-tftp.c output-json-tftp.h \
output-json-template.c output-json-template.h \
output-json-vars.c output-json-vars.h \
output-lua.c output-lua.h \
printf(" alproto: ALPROTO_NFS\n");
else if (pp_pe->alproto == ALPROTO_NTP)
printf(" alproto: ALPROTO_NTP\n");
+ else if (pp_pe->alproto == ALPROTO_TFTP)
+ printf(" alproto: ALPROTO_TFTP\n");
else if (pp_pe->alproto == ALPROTO_TEMPLATE)
printf(" alproto: ALPROTO_TEMPLATE\n");
else if (pp_pe->alproto == ALPROTO_DNP3)
printf(" alproto: ALPROTO_NFS\n");
else if (pp_pe->alproto == ALPROTO_NTP)
printf(" alproto: ALPROTO_NTP\n");
+ else if (pp_pe->alproto == ALPROTO_TFTP)
+ printf(" alproto: ALPROTO_TFTP\n");
else if (pp_pe->alproto == ALPROTO_TEMPLATE)
printf(" alproto: ALPROTO_TEMPLATE\n");
else if (pp_pe->alproto == ALPROTO_DNP3)
#include "app-layer-nfs-tcp.h"
#include "app-layer-nfs-udp.h"
#include "app-layer-ntp.h"
+#include "app-layer-tftp.h"
#include "app-layer-template.h"
#include "conf.h"
RegisterNFSTCPParsers();
RegisterNFSUDPParsers();
RegisterNTPParsers();
+ RegisterTFTPParsers();
RegisterTemplateParsers();
/** IMAP */
break;
case ALPROTO_FTPDATA:
proto_name = "ftp-data";
+ case ALPROTO_TFTP:
+ proto_name = "tftp";
break;
case ALPROTO_TEMPLATE:
proto_name = "template";
ALPROTO_NFS,
ALPROTO_NTP,
ALPROTO_FTPDATA,
+ ALPROTO_TFTP,
ALPROTO_TEMPLATE,
/* used by the probing parser when alproto detection fails
--- /dev/null
+/* Copyright (C) 2017 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 Clément Galland <clement.galland@epita.fr>
+ *
+ * Parser for NTP application layer running on UDP port 69.
+ */
+
+
+#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-tftp.h"
+
+#ifndef HAVE_RUST
+
+void RegisterTFTPParsers(void)
+{
+}
+
+#else
+
+#include "rust-tftp-tftp-gen.h"
+
+/* The default port to probe if not provided in the configuration file. */
+#define TFTP_DEFAULT_PORT "69"
+
+/* The minimum size for an message. For some protocols this might
+ * be the size of a header. */
+#define TFTP_MIN_FRAME_LEN 4
+
+/* 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 tftp any any -> any any (msg:"SURICATA TFTP empty message"; \
+ * app-layer-event:tftp.empty_message; sid:X; rev:Y;)
+ */
+enum {
+ TFTP_DECODER_EVENT_EMPTY_MESSAGE,
+};
+
+SCEnumCharMap tftp_decoder_event_table[] = {
+ {"EMPTY_MESSAGE", TFTP_DECODER_EVENT_EMPTY_MESSAGE},
+
+ // event table must be NULL-terminated
+ { NULL, -1 },
+};
+
+static void *TFTPStateAlloc(void)
+{
+ return rs_tftp_state_alloc();
+}
+
+static void TFTPStateFree(void *state)
+{
+ rs_tftp_state_free(state);
+}
+
+/**
+ * \brief Callback from the application layer to have a transaction freed.
+ *
+ * \param state a void pointer to the TFTPState object.
+ * \param tx_id the transaction ID to free.
+ */
+static void TFTPStateTxFree(void *state, uint64_t tx_id)
+{
+ rs_tftp_state_tx_free(state, tx_id);
+}
+
+static int TFTPStateGetEventInfo(const char *event_name, int *event_id,
+ AppLayerEventType *event_type)
+{
+ return -1;
+}
+
+static AppLayerDecoderEvents *TFTPGetEvents(void *state, uint64_t tx_id)
+{
+ return NULL;
+}
+
+static int TFTPHasEvents(void *state)
+{
+ return rs_tftp_has_event(state);
+}
+
+/**
+ * \brief Probe the input to see if it looks like echo.
+ *
+ * \retval ALPROTO_TFTP if it looks like echo, otherwise
+ * ALPROTO_UNKNOWN.
+ */
+static AppProto TFTPProbingParser(Flow *f, uint8_t *input, uint32_t input_len,
+ uint32_t *offset)
+{
+ /* Very simple test - if there is input, this is echo.
+ * Also check if it's starting by a zero */
+ if (input_len >= TFTP_MIN_FRAME_LEN && *input == 0) {
+ SCLogDebug("Detected as ALPROTO_TFTP.");
+ return ALPROTO_TFTP;
+ }
+
+ SCLogDebug("Protocol not detected as ALPROTO_TFTP.");
+ return ALPROTO_UNKNOWN;
+}
+
+static int TFTPParseRequest(Flow *f, void *state,
+ AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
+ void *local_data)
+{
+ SCLogDebug("Parsing echo request: len=%"PRIu32, input_len);
+
+ /* Likely connection closed, we can just return here. */
+ if ((input == NULL || input_len == 0) &&
+ AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF)) {
+ return 0;
+ }
+
+ /* Probably don't want to create a transaction in this case
+ * either. */
+ if (input == NULL || input_len == 0) {
+ return 0;
+ }
+
+ return rs_tftp_request(state, input, input_len);
+}
+
+/**
+ * \brief Response parsing is not implemented
+ */
+static int TFTPParseResponse(Flow *f, void *state, AppLayerParserState *pstate,
+ uint8_t *input, uint32_t input_len, void *local_data)
+{
+ return 0;
+}
+
+static uint64_t TFTPGetTxCnt(void *state)
+{
+ return rs_tftp_get_tx_cnt(state);
+}
+
+static void *TFTPGetTx(void *state, uint64_t tx_id)
+{
+ return rs_tftp_get_tx(state, tx_id);
+}
+
+static void TFTPSetTxLogged(void *state, void *vtx, uint32_t logger)
+{
+ rs_tftp_set_tx_logged(state, vtx, logger);
+}
+
+static int TFTPGetTxLogged(void *state, void *vtx, uint32_t logger)
+{
+ return rs_tftp_get_tx_logged(state, vtx, logger);
+}
+
+/**
+ * \brief Called by the application layer.
+ *
+ * In most cases 1 can be returned here.
+ */
+static int TFTPGetAlstateProgressCompletionStatus(uint8_t direction) {
+ return 1;
+}
+
+/**
+ * \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 TFTPGetStateProgress(void *tx, uint8_t direction)
+{
+ return 1;
+}
+
+static DetectEngineState *TFTPGetTxDetectState(void *vtx)
+{
+ return NULL;
+}
+
+static int TFTPSetTxDetectState(void *state, void *vtx,
+ DetectEngineState *s)
+{
+ return 0;
+}
+
+void RegisterTFTPParsers(void)
+{
+ const char *proto_name = "tftp";
+
+ /* Check if TFTP UDP detection is enabled. If it does not exist in
+ * the configuration file then it will be enabled by default. */
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
+
+ SCLogDebug("TFTP UDP protocol detection enabled.");
+
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_TFTP, proto_name);
+
+ if (RunmodeIsUnittests()) {
+ SCLogDebug("Unittest mode, registeringd default configuration.");
+ AppLayerProtoDetectPPRegister(IPPROTO_UDP, TFTP_DEFAULT_PORT,
+ ALPROTO_TFTP, 0, TFTP_MIN_FRAME_LEN,
+ STREAM_TOSERVER, TFTPProbingParser,
+ NULL);
+ } else {
+ if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP,
+ proto_name, ALPROTO_TFTP,
+ 0, TFTP_MIN_FRAME_LEN,
+ TFTPProbingParser, NULL)) {
+ SCLogDebug("No echo app-layer configuration, enabling echo"
+ " detection UDP detection on port %s.",
+ TFTP_DEFAULT_PORT);
+ AppLayerProtoDetectPPRegister(IPPROTO_UDP,
+ TFTP_DEFAULT_PORT, ALPROTO_TFTP,
+ 0, TFTP_MIN_FRAME_LEN,
+ STREAM_TOSERVER,TFTPProbingParser,
+ NULL);
+ }
+ }
+ } else {
+ SCLogDebug("Protocol detecter and parser disabled for TFTP.");
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("udp", proto_name)) {
+
+ SCLogDebug("Registering TFTP protocol parser.");
+
+ /* Register functions for state allocation and freeing. A
+ * state is allocated for every new TFTP flow. */
+ AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPStateAlloc, TFTPStateFree);
+
+ /* Register request parser for parsing frame from server to client. */
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_TFTP,
+ STREAM_TOSERVER, TFTPParseRequest);
+
+ /* Register response parser for parsing frames from server to client. */
+ AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_TFTP,
+ STREAM_TOCLIENT, TFTPParseResponse);
+
+ /* Register a function to be called by the application layer
+ * when a transaction is to be freed. */
+ AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPStateTxFree);
+
+ AppLayerParserRegisterLoggerFuncs(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPGetTxLogged, TFTPSetTxLogged);
+
+ /* Register a function to return the current transaction count. */
+ AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPGetTxCnt);
+
+ /* Transaction handling. */
+ AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_TFTP,
+ TFTPGetAlstateProgressCompletionStatus);
+ AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP,
+ ALPROTO_TFTP,
+ TFTPGetStateProgress);
+ AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPGetTx);
+
+ /* Application layer event handling. */
+ AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPHasEvents);
+
+ /* What is this being registered for? */
+ AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_TFTP,
+ NULL, TFTPGetTxDetectState,
+ TFTPSetTxDetectState);
+
+ AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPStateGetEventInfo);
+ AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPGetEvents);
+ }
+ else {
+ SCLogDebug("TFTP protocol parsing disabled.");
+ }
+
+#ifdef UNITTESTS
+ AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_TFTP,
+ TFTPParserRegisterTests);
+#endif
+}
+
+#ifdef UNITTESTS
+#endif
+
+void TFTPParserRegisterTests(void)
+{
+#ifdef UNITTESTS
+#endif
+}
+
+#endif /* HAVE_RUST */
--- /dev/null
+/* Copyright (C) 2017 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 Clément Galland <clement.galland@epita.fr>
+ */
+
+#ifndef __APP_LAYER_TFTP_H__
+#define __APP_LAYER_TFTP_H__
+
+#include "detect-engine-state.h"
+
+#include "queue.h"
+
+void RegisterTFTPParsers(void);
+void TFTPParserRegisterTests(void);
+
+/** Opaque Rust types. */
+typedef struct TFTPState_ TFTPState;
+typedef struct TFTPTransaction_ TFTPTransaction;
+
+#endif /* __APP_LAYER_TFTP_H__ */
--- /dev/null
+/* Copyright (C) 2017 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 Clément Galland <clement.galland@epita.fr>
+ *
+ * Implement JSON/eve logging app-layer TFTP.
+ */
+
+//#ifdef HAVE_RUST
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-unittest.h"
+#include "util-buffer.h"
+#include "util-debug.h"
+#include "util-byte.h"
+
+#include "output.h"
+#include "output-json.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+
+#include "app-layer-tftp.h"
+#include "output-json-tftp.h"
+
+#ifdef HAVE_RUST
+
+#include "rust.h"
+#include "rust-tftp-log-gen.h"
+
+#ifdef HAVE_LIBJANSSON
+
+typedef struct LogTFTPFileCtx_ {
+ LogFileCtx *file_ctx;
+ uint32_t flags;
+} LogTFTPFileCtx;
+
+typedef struct LogTFTPLogThread_ {
+ LogTFTPFileCtx *tftplog_ctx;
+ uint32_t count;
+ MemBuffer *buffer;
+} LogTFTPLogThread;
+
+static int JsonTFTPLogger(ThreadVars *tv, void *thread_data,
+ const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id)
+{
+ LogTFTPLogThread *thread = thread_data;
+ json_t *js, *tftpjs;
+
+ js = CreateJSONHeader((Packet *)p, 0, "tftp");
+ if (unlikely(js == NULL)) {
+ return TM_ECODE_FAILED;
+ }
+
+ tftpjs = rs_tftp_log_json_request(tx);
+ if (unlikely(tftpjs == NULL)) {
+ goto error;
+ }
+
+ json_object_set_new(js, "tftp", tftpjs);
+
+ MemBufferReset(thread->buffer);
+ OutputJSONBuffer(js, thread->tftplog_ctx->file_ctx, &thread->buffer);
+
+ json_decref(js);
+ return TM_ECODE_OK;
+
+error:
+ json_decref(js);
+ return TM_ECODE_FAILED;
+}
+
+static void OutputTFTPLogDeInitCtxSub(OutputCtx *output_ctx)
+{
+ LogTFTPFileCtx *tftplog_ctx = (LogTFTPFileCtx *)output_ctx->data;
+ SCFree(tftplog_ctx);
+ SCFree(output_ctx);
+}
+
+static OutputCtx *OutputTFTPLogInitSub(ConfNode *conf,
+ OutputCtx *parent_ctx)
+{
+ OutputJsonCtx *ajt = parent_ctx->data;
+
+ LogTFTPFileCtx *tftplog_ctx = SCCalloc(1, sizeof(*tftplog_ctx));
+ if (unlikely(tftplog_ctx == NULL)) {
+ return NULL;
+ }
+ tftplog_ctx->file_ctx = ajt->file_ctx;
+
+ OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
+ if (unlikely(output_ctx == NULL)) {
+ SCFree(tftplog_ctx);
+ return NULL;
+ }
+ output_ctx->data = tftplog_ctx;
+ output_ctx->DeInit = OutputTFTPLogDeInitCtxSub;
+
+ SCLogDebug("TFTP log sub-module initialized.");
+
+ AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_TFTP);
+
+ return output_ctx;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+
+static TmEcode JsonTFTPLogThreadInit(ThreadVars *t, const void *initdata, void **data)
+{
+ LogTFTPLogThread *thread = SCCalloc(1, sizeof(*thread));
+ if (unlikely(thread == NULL)) {
+ return TM_ECODE_FAILED;
+ }
+
+ if (initdata == NULL) {
+ SCLogDebug("Error getting context for EveLogTFTP. \"initdata\" is NULL.");
+ SCFree(thread);
+ return TM_ECODE_FAILED;
+ }
+
+ thread->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+ if (unlikely(thread->buffer == NULL)) {
+ SCFree(thread);
+ return TM_ECODE_FAILED;
+ }
+
+ thread->tftplog_ctx = ((OutputCtx *)initdata)->data;
+ *data = (void *)thread;
+
+ return TM_ECODE_OK;
+}
+
+static TmEcode JsonTFTPLogThreadDeinit(ThreadVars *t, void *data)
+{
+ LogTFTPLogThread *thread = (LogTFTPLogThread *)data;
+ if (thread == NULL) {
+ return TM_ECODE_OK;
+ }
+ if (thread->buffer != NULL) {
+ MemBufferFree(thread->buffer);
+ }
+ SCFree(thread);
+ return TM_ECODE_OK;
+}
+
+void JsonTFTPLogRegister(void)
+{
+ /* Register as an eve sub-module. */
+ OutputRegisterTxSubModule(LOGGER_JSON_TFTP, "eve-log", "JsonTFTPLog",
+ "eve-log.tftp", OutputTFTPLogInitSub,
+ ALPROTO_TFTP, JsonTFTPLogger,
+ JsonTFTPLogThreadInit, JsonTFTPLogThreadDeinit,
+ NULL);
+
+ SCLogDebug("TFTP JSON logger registered.");
+}
+#else /* HAVE_RUST */
+
+void JsonTFTPLogRegister(void)
+{
+}
+
+#endif /* HAVE_RUST */
+
+#else /* HAVE_LIBJANSSON */
+
+void JsonTFTPLogRegister(void)
+{
+}
+
+#endif /* HAVE_LIBJANSSON */
--- /dev/null
+/* Copyright (C) 2017 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 Clément Galland <clement.galland@epita.fr>
+ */
+
+#ifndef __OUTPUT_JSON_TFTP_H__
+#define __OUTPUT_JSON_TFTP_H__
+
+void JsonTFTPLogRegister(void);
+
+#endif /* __OUTPUT_JSON_TFTP_H__ */
#include "log-stats.h"
#include "output-json.h"
#include "output-json-nfs.h"
+#include "output-json-tftp.h"
#include "output-json-template.h"
#include "output-lua.h"
#include "output-json-dnp3.h"
/* NFS JSON logger. */
JsonNFSLogRegister();
+ /* TFTP JSON logger. */
+ JsonTFTPLogRegister();
/* Template JSON logger. */
JsonTemplateLogRegister();
}
LOGGER_JSON_SMTP,
LOGGER_JSON_TLS,
LOGGER_JSON_NFS,
+ LOGGER_JSON_TFTP,
LOGGER_JSON_DNP3_TS,
LOGGER_JSON_DNP3_TC,
LOGGER_JSON_SSH,