diff --git a/.github/workflows/authors-done.yml b/.github/workflows/authors-done.yml index b1700e1d030d..d0fdf4491c45 100644 --- a/.github/workflows/authors-done.yml +++ b/.github/workflows/authors-done.yml @@ -44,8 +44,7 @@ jobs: script: | let fs = require('fs'); let issue_number = Number(fs.readFileSync('./pr-number.txt')); - let new_authors = String(fs.readFileSync('./new-authors.txt')); - let msg = 'NOTE: This PR may contain new authors:\n\n```\n' + new_authors + '```'; + let msg = 'NOTE: This PR may contain new authors.'; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 62748b10e985..93708415294a 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -12,6 +12,10 @@ on: SV_REPO: SV_BRANCH: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + permissions: read-all env: @@ -284,6 +288,18 @@ jobs: test -e /usr/local/lib/suricata/python/suricata/update/configs/modify.conf test -e /usr/local/lib/suricata/python/suricata/update/configs/threshold.in test -e /usr/local/lib/suricata/python/suricata/update/configs/update.yaml + - name: Build C json filetype plugin + working-directory: examples/plugins/c-json-filetype + run: make + - name: Check C json filetype plugin + run: test -e examples/plugins/c-json-filetype/.libs/json-filetype.so.0.0.0 + - name: Installing headers and library + run: | + make install-headers + make install-library + - name: Test plugin build with Makefile.example + working-directory: examples/plugins/c-json-filetype + run: PATH=/usr/local/bin:$PATH make -f Makefile.example almalinux-9-templates: name: AlmaLinux 9 Test Templates diff --git a/Makefile.am b/Makefile.am index 67963ed32fcf..b7a221f49299 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST = ChangeLog COPYING LICENSE suricata.yaml.in \ scripts/generate-images.sh SUBDIRS = $(HTP_DIR) rust src qa rules doc contrib etc python ebpf \ $(SURICATA_UPDATE_DIR) +DIST_SUBDIRS = examples/plugins/c-json-filetype $(SUBDIRS) CLEANFILES = stamp-h[0-9]* diff --git a/configure.ac b/configure.ac index 1908c76ab76a..1eb054d98c1e 100644 --- a/configure.ac +++ b/configure.ac @@ -2613,6 +2613,8 @@ AC_CONFIG_FILES(suricata.yaml etc/Makefile etc/suricata.logrotate etc/suricata.s AC_CONFIG_FILES(python/Makefile python/suricata/config/defaults.py) AC_CONFIG_FILES(ebpf/Makefile) AC_CONFIG_FILES(libsuricata-config) +AC_CONFIG_FILES(examples/plugins/c-json-filetype/Makefile) + AC_OUTPUT SURICATA_BUILD_CONF="Suricata Configuration: diff --git a/doc/userguide/output/eve/eve-json-format.rst b/doc/userguide/output/eve/eve-json-format.rst index a253e046cf7f..5a29960857a3 100644 --- a/doc/userguide/output/eve/eve-json-format.rst +++ b/doc/userguide/output/eve/eve-json-format.rst @@ -735,8 +735,8 @@ If extended logging is enabled the following fields are also included: * "fingerprint": The (SHA1) fingerprint of the TLS certificate * "sni": The Server Name Indication (SNI) extension sent by the client * "version": The SSL/TLS version used -* "not_before": The NotBefore field from the TLS certificate -* "not_after": The NotAfter field from the TLS certificate +* "notbefore": The NotBefore field from the TLS certificate +* "notafter": The NotAfter field from the TLS certificate * "ja3": The JA3 fingerprint consisting of both a JA3 hash and a JA3 string * "ja3s": The JA3S fingerprint consisting of both a JA3 hash and a JA3 string diff --git a/doc/userguide/rules/mqtt-keywords.rst b/doc/userguide/rules/mqtt-keywords.rst index 21776ac357cb..058a17b7ffde 100644 --- a/doc/userguide/rules/mqtt-keywords.rst +++ b/doc/userguide/rules/mqtt-keywords.rst @@ -163,6 +163,19 @@ Examples:: ``mqtt.connect.password`` is a 'sticky buffer' and can be used as ``fast_pattern``. +mqtt.connect.protocol_string +---------------------------- + +Match on the protocol string in the MQTT CONNECT message. In contrast to ``mqtt.protocol_version`` this is a property that is only really relevant in the initial CONNECT communication and never used again; hence it is organized under ``mqtt.connect``. + +Examples:: + + mqtt.connect.protocol_string; content:"MQTT"; + mqtt.connect.protocol_string; content:"MQIsdp"; + +``mqtt.connect.protocol_string`` is a 'sticky buffer' and can be used as ``fast_pattern``. + + mqtt.connect.username --------------------- diff --git a/examples/plugins/README.md b/examples/plugins/README.md new file mode 100644 index 000000000000..8e47b6e7ffc2 --- /dev/null +++ b/examples/plugins/README.md @@ -0,0 +1,6 @@ +# Example Plugins + +## c-json-filetype + +An example plugin of an EVE/JSON filetype plugin. This type of plugin +is useful if you want to send EVE output to custom destinations. diff --git a/examples/plugins/c-json-filetype/.gitignore b/examples/plugins/c-json-filetype/.gitignore new file mode 100644 index 000000000000..f5bb3af73f70 --- /dev/null +++ b/examples/plugins/c-json-filetype/.gitignore @@ -0,0 +1,2 @@ +*.so +*.la diff --git a/examples/plugins/c-json-filetype/Makefile.am b/examples/plugins/c-json-filetype/Makefile.am new file mode 100644 index 000000000000..d5e912b8a469 --- /dev/null +++ b/examples/plugins/c-json-filetype/Makefile.am @@ -0,0 +1,17 @@ +plugindir = ${libdir}/suricata/plugins + +if BUILD_SHARED_LIBRARY +plugin_LTLIBRARIES = json-filetype.la +json_filetype_la_LDFLAGS = -module -shared +json_filetype_la_SOURCES = filetype.c + +json_filetype_la_CPPFLAGS = -I$(abs_top_srcdir)/rust/gen -I$(abs_top_srcdir)/rust/dist + +else + +all-local: + @echo + @echo "Shared library support must be enabled to build plugins." + @echo + +endif diff --git a/examples/plugins/c-json-filetype/Makefile.example b/examples/plugins/c-json-filetype/Makefile.example new file mode 100644 index 000000000000..6d514aab1445 --- /dev/null +++ b/examples/plugins/c-json-filetype/Makefile.example @@ -0,0 +1,18 @@ +SRCS := filetype.c + +LIBSURICATA_CONFIG ?= libsuricata-config + +CPPFLAGS += `$(LIBSURICATA_CONFIG) --cflags` +CPPFLAGS += -DSURICATA_PLUGIN -I. +CPPFLAGS += "-D__SCFILENAME__=\"$(*F)\"" + +OBJS := $(SRCS:.c=.o) + +filetype.so: $(OBJS) + $(CC) -fPIC -shared -o $@ $(OBJS) + +%.o: %.c + $(CC) -fPIC $(CPPFLAGS) -c -o $@ $< + +clean: + rm -f *.o *.so *~ diff --git a/examples/plugins/c-json-filetype/README.md b/examples/plugins/c-json-filetype/README.md new file mode 100644 index 000000000000..2f7978977dbd --- /dev/null +++ b/examples/plugins/c-json-filetype/README.md @@ -0,0 +1,123 @@ +# Example EVE Filetype Plugin + +## Building + +If in the Suricata source directory, this plugin can be built by +running `make` and installed with `make install`. + +Note that Suricata must have been built without `--disable-shared`. + +## Building Standalone + +The file `Makefile.example` is an example of how you might build a +plugin that is distributed separately from the Suricata source code. + +It has the following dependencies: + +- Suricata is installed +- The Suricata library is installed: `make install-library` +- The Suricata development headers are installed: `make install-headers` +- The program `libsuricata-config` is in your path (installed with + `make install-library`) + +The run: `make -f Makefile.example` + +Before building this plugin you will need to build and install Suricata from the +git master branch and install the development tools and headers: + +- `make install-library` +- `make install-headers` + +then make sure the newly installed tool `libsuricata-config` can be +found in your path, for example: +``` +libsuricata-config --cflags +``` + +Then a simple `make` should build this plugin. + +Or if the Suricata installation is not in the path, a command like the following +can be used: + +``` +PATH=/opt/suricata/bin:$PATH make +``` + +## Usage + +To run the plugin, first add the path to the plugin you just compiled to +your `suricata.yaml`, for example: +``` +plugins: + - /usr/lib/suricata/plugins/json-filetype.so +``` + +Then add an output for the plugin: +``` +outputs: + - eve-log: + enabled: yes + filetype: json-filetype-plugin + threaded: true + types: + - dns + - tls + - http +``` + +In the example above we use the name specified in the plugin as the `filetype` +and specify that all `dns`, `tls` and `http` log entries should be sent to the +plugin. + +## Details + +This plugin demonstrates a Suricata JSON/EVE output plugin +(file-type). The idea of a Suricata EVE output plugin is to provide a +file like interface for the handling of rendered JSON logs. This is +useful for custom destinations not builtin to Suricata or if the +formatted JSON requires some post-processing. + +Note: EVE output plugins are not that useful just for reformatting the +JSON output as the plugin does need to handle writing to a file once +the file type has been delegated to the plugin. + +### Registering a Plugin + +All Suricata plugins make themselves known to Suricata by using a +function named `SCPluginRegister` which is called after Suricata loads +the plugin shared object file. This function must return a `SCPlugin` +struct which contains basic information about the plugin. For +example: + +```c +const SCPlugin PluginRegistration = { + .name = "eve-filetype", + .author = "Jason Ish", + .license = "GPLv2", + .Init = TemplateInit, +}; + +const SCPlugin *SCPluginRegister() { + return &PluginRegistration; +} +``` + +### Initializing a Plugin + +After the plugin has been registered, the `Init` callback will be called. This +is where the plugin will set itself up as a specific type of plugin such as an +EVE output, or a capture method. + +This plugins registers itself as an EVE file type using the +`SCRegisterEveFileType` struct. To register as an EVE file type the +following must be provided: + +* name: This is the name of the output which will be used in the eve filetype + field in `suricata.yaml` to enable this output. +* Init: The callback called when the output is "opened". +* Deinit: The callback called the output is "closed". +* ThreadInit: Callback called to initialize per thread data (if threaded). +* ThreadDeinit: Callback called to deinitialize per thread data (if threaded). +* Write: The callback called when an EVE record is to be "written". + +Please see the code in `filetype.c` for more details about this functions. diff --git a/examples/plugins/c-json-filetype/filetype.c b/examples/plugins/c-json-filetype/filetype.c new file mode 100644 index 000000000000..9c81d7f03267 --- /dev/null +++ b/examples/plugins/c-json-filetype/filetype.c @@ -0,0 +1,243 @@ +/* Copyright (C) 2020-2023 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. + */ + +#include "suricata-common.h" +#include "suricata-plugin.h" +#include "util-mem.h" +#include "util-debug.h" + +#define FILETYPE_NAME "json-filetype-plugin" + +static int FiletypeThreadInit(void *ctx, int thread_id, void **thread_data); +static int FiletypeThreadDeinit(void *ctx, void *thread_data); + +/** + * Per thread context data for each logging thread. + */ +typedef struct ThreadData_ { + /** The thread ID, for demonstration purposes only. */ + int thread_id; + + /** The number of records logged on this thread. */ + uint64_t count; +} ThreadData; + +/** + * A context object for each eve logger using this output. + */ +typedef struct Context_ { + /** Verbose, or print to stdout. */ + int verbose; + + /** A thread context to use when not running in threaded mode. */ + ThreadData *thread; +} Context; + +/** + * This function is called to initialize the output, it can be somewhat thought + * of like opening a file. + * + * \param conf The EVE configuration node using this output. + * + * \param threaded If true the EVE subsystem is running in threaded mode. + * + * \param data A pointer where context data can be stored relevant to this + * output. + * + * Eve output plugins need to be thread aware as the threading happens at lower + * level than the EVE output, so a flag is provided here to notify the plugin if + * threading is enabled or not. + * + * If the plugin does not work with threads disabled, or enabled, this function + * should return -1. + * + * Note for upgrading a plugin from 6.0 to 7.0: The ConfNode in 7.0 is the + * configuration for the eve instance, not just a node named after the plugin. + * This allows the plugin to get more context about what it is logging. + */ +static int FiletypeInit(ConfNode *conf, bool threaded, void **data) +{ + SCLogNotice("Initializing template eve output plugin: threaded=%d", threaded); + Context *context = SCCalloc(1, sizeof(Context)); + if (context == NULL) { + return -1; + } + + /* Verbose by default. */ + int verbose = 1; + + /* An example of how you can access configuration data from a + * plugin. */ + if (conf && (conf = ConfNodeLookupChild(conf, "eve-template")) != NULL) { + if (!ConfGetChildValueBool(conf, "verbose", &verbose)) { + verbose = 1; + } else { + SCLogNotice("Read verbose configuration value of %d", verbose); + } + } + context->verbose = verbose; + + if (!threaded) { + /* We're not running in threaded mode so allocate a thread context here + * to avoid duplication of context data such as file pointers, database + * connections, etc. */ + if (FiletypeThreadInit(context, 0, (void **)&context->thread) != 0) { + SCFree(context); + return -1; + } + } + *data = context; + return 0; +} + +/** + * This function is called when the output is closed. + * + * This will be called after ThreadDeinit is called for each thread. + * + * \param data The data allocated in FiletypeInit. It should be cleaned up and + * deallocated here. + */ +static void FiletypeDeinit(void *data) +{ + printf("TemplateClose\n"); + Context *ctx = data; + if (ctx != NULL) { + if (ctx->thread) { + FiletypeThreadDeinit(ctx, (void *)ctx->thread); + } + SCFree(ctx); + } +} + +/** + * Initialize per thread context. + * + * \param ctx The context created in TemplateInitOutput. + * + * \param thread_id An identifier for this thread. + * + * \param thread_data Pointer where thread specific context can be stored. + * + * When the EVE output is running in threaded mode this will be called once for + * each output thread with a unique thread_id. For regular file logging in + * threaded mode Suricata uses the thread_id to construct the files in the form + * of "eve..json". This plugin may want to do similar, or open + * multiple connections to whatever the final logging location might be. + * + * In the case of non-threaded EVE logging this function is NOT called by + * Suricata, but instead this plugin chooses to use this method to create a + * default (single) thread context. + */ +static int FiletypeThreadInit(void *ctx, int thread_id, void **thread_data) +{ + ThreadData *tdata = SCCalloc(1, sizeof(ThreadData)); + if (tdata == NULL) { + SCLogError("Failed to allocate thread data"); + return -1; + } + tdata->thread_id = thread_id; + *thread_data = tdata; + SCLogNotice( + "Initialized thread %03d (pthread_id=%" PRIuMAX ")", tdata->thread_id, pthread_self()); + return 0; +} + +/** + * Deinitialize a thread. + * + * This is where any cleanup per thread should be done including free'ing of the + * thread_data if needed. + */ +static int FiletypeThreadDeinit(void *ctx, void *thread_data) +{ + if (thread_data == NULL) { + // Nothing to do. + return 0; + } + + ThreadData *tdata = thread_data; + SCLogNotice( + "Deinitializing thread %d: records written: %" PRIu64, tdata->thread_id, tdata->count); + SCFree(tdata); + return 0; +} + +/** + * This method is called with formatted Eve JSON data. + * + * \param buffer Formatted JSON buffer \param buffer_len Length of formatted + * JSON buffer \param data Data set in Init callback \param thread_data Data set + * in ThreadInit callbacl + * + * Do not block in this thread, it will cause packet loss. Instead of outputting + * to any resource that may block it might be best to enqueue the buffers for + * further processing which will require copying of the provided buffer. + */ +static int FiletypeWrite(const char *buffer, int buffer_len, void *data, void *thread_data) +{ + Context *ctx = data; + ThreadData *thread = thread_data; + + /* The thread_data could be null which is valid, or it could be that we are + * in single threaded mode. */ + if (thread == NULL) { + thread = ctx->thread; + } + + thread->count++; + + if (ctx->verbose) { + SCLogNotice("Received write with thread_data %p: %s", thread_data, buffer); + } + return 0; +} + +/** + * Called by Suricata to initialize the module. This module registers + * new file type to the JSON logger. + */ +void PluginInit(void) +{ + SCEveFileType *my_output = SCCalloc(1, sizeof(SCEveFileType)); + my_output->name = FILETYPE_NAME; + my_output->Init = FiletypeInit; + my_output->Deinit = FiletypeDeinit; + my_output->ThreadInit = FiletypeThreadInit; + my_output->ThreadDeinit = FiletypeThreadDeinit; + my_output->Write = FiletypeWrite; + if (!SCRegisterEveFileType(my_output)) { + FatalError("Failed to register filetype plugin: %s", FILETYPE_NAME); + } +} + +const SCPlugin PluginRegistration = { + .name = FILETYPE_NAME, + .author = "FirstName LastName ", + .license = "GPL-2.0-only", + .Init = PluginInit, +}; + +/** + * The function called by Suricata after loading this plugin. + * + * A pointer to a populated SCPlugin struct must be returned. + */ +const SCPlugin *SCPluginRegister() +{ + return &PluginRegistration; +} diff --git a/rust/src/applayertemplate/logger.rs b/rust/src/applayertemplate/logger.rs index 0105526fc0f1..766a07acdb9d 100644 --- a/rust/src/applayertemplate/logger.rs +++ b/rust/src/applayertemplate/logger.rs @@ -20,12 +20,14 @@ use crate::jsonbuilder::{JsonBuilder, JsonError}; use std; fn log_template(tx: &TemplateTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> { + js.open_object("template")?; if let Some(ref request) = tx.request { js.set_string("request", request)?; } if let Some(ref response) = tx.response { js.set_string("response", response)?; } + js.close()?; Ok(()) } diff --git a/rust/src/bittorrent_dht/logger.rs b/rust/src/bittorrent_dht/logger.rs index 2cfb9270ef67..74ea7c59ba57 100644 --- a/rust/src/bittorrent_dht/logger.rs +++ b/rust/src/bittorrent_dht/logger.rs @@ -48,6 +48,7 @@ fn print_ip_addr(addr: &[u8]) -> std::string::String { fn log_bittorrent_dht( tx: &BitTorrentDHTTransaction, js: &mut JsonBuilder, ) -> Result<(), JsonError> { + js.open_object("bittorrent_dht")?; js.set_hex("transaction_id", &tx.transaction_id)?; if let Some(client_version) = &tx.client_version { js.set_hex("client_version", client_version)?; @@ -125,6 +126,7 @@ fn log_bittorrent_dht( } js.close()?; }; + js.close()?; Ok(()) } diff --git a/rust/src/http2/logger.rs b/rust/src/http2/logger.rs index d25f852c43ab..099112b1aeb4 100644 --- a/rust/src/http2/logger.rs +++ b/rust/src/http2/logger.rs @@ -192,6 +192,7 @@ fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result Result { + js.open_object("http")?; js.set_string("version", "2")?; let mut common: HashMap> = HashMap::new(); @@ -261,8 +262,8 @@ fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result Result<(), JsonError> { + jsb.open_object("krb5")?; match tx.error_code { Some(c) => { jsb.set_string("msg_type", &format!("{:?}", tx.msg_type))?; @@ -63,12 +64,13 @@ fn krb5_log_response(jsb: &mut JsonBuilder, tx: &mut KRB5Transaction) -> Result< jsb.set_string("ticket_encryption", &refs)?; jsb.set_bool("ticket_weak_encryption", test_weak_encryption(x))?; } + jsb.close()?; return Ok(()); } #[no_mangle] -pub extern "C" fn rs_krb5_log_json_response(jsb: &mut JsonBuilder, tx: &mut KRB5Transaction) -> bool +pub extern "C" fn rs_krb5_log_json_response(tx: &mut KRB5Transaction, jsb: &mut JsonBuilder) -> bool { krb5_log_response(jsb, tx).is_ok() } diff --git a/rust/src/mqtt/detect.rs b/rust/src/mqtt/detect.rs index b47a84f74409..df0c78e8497f 100644 --- a/rust/src/mqtt/detect.rs +++ b/rust/src/mqtt/detect.rs @@ -231,6 +231,26 @@ pub unsafe extern "C" fn rs_mqtt_tx_get_connect_willmessage( return 0; } +#[no_mangle] +pub unsafe extern "C" fn rs_mqtt_tx_get_connect_protocol_string( + tx: &MQTTTransaction, buffer: *mut *const u8, buffer_len: *mut u32, +) -> u8 { + for msg in tx.msg.iter() { + if let MQTTOperation::CONNECT(ref cv) = msg.op { + let p = &cv.protocol_string; + if !p.is_empty() { + *buffer = p.as_ptr(); + *buffer_len = p.len() as u32; + return 1; + } + } + } + + *buffer = ptr::null(); + *buffer_len = 0; + return 0; +} + #[no_mangle] pub unsafe extern "C" fn rs_mqtt_tx_get_connack_sessionpresent( tx: &MQTTTransaction, session_present: *mut bool, diff --git a/rust/src/quic/logger.rs b/rust/src/quic/logger.rs index e03ebdd6bf21..8cb08830e4d0 100644 --- a/rust/src/quic/logger.rs +++ b/rust/src/quic/logger.rs @@ -88,7 +88,7 @@ fn quic_tls_extension_name(e: u16) -> Option { } } -fn log_template(tx: &QuicTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> { +fn log_quic(tx: &QuicTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> { js.open_object("quic")?; if tx.header.ty != QuicType::Short { js.set_string("version", String::from(tx.header.version).as_str())?; @@ -153,5 +153,5 @@ pub unsafe extern "C" fn rs_quic_to_json( tx: *mut std::os::raw::c_void, js: &mut JsonBuilder, ) -> bool { let tx = cast_pointer!(tx, QuicTransaction); - log_template(tx, js).is_ok() + log_quic(tx, js).is_ok() } diff --git a/rust/src/snmp/log.rs b/rust/src/snmp/log.rs index 83414816c466..5707f30ccb4e 100644 --- a/rust/src/snmp/log.rs +++ b/rust/src/snmp/log.rs @@ -39,6 +39,7 @@ fn str_of_pdu_type(t:&PduType) -> Cow { fn snmp_log_response(jsb: &mut JsonBuilder, tx: &mut SNMPTransaction) -> Result<(), JsonError> { + jsb.open_object("snmp")?; jsb.set_uint("version", tx.version as u64)?; if tx.encrypted { jsb.set_string("pdu_type", "encrypted")?; @@ -71,11 +72,12 @@ fn snmp_log_response(jsb: &mut JsonBuilder, tx: &mut SNMPTransaction) -> Result< } } + jsb.close()?; return Ok(()); } #[no_mangle] -pub extern "C" fn rs_snmp_log_json_response(jsb: &mut JsonBuilder, tx: &mut SNMPTransaction) -> bool +pub extern "C" fn rs_snmp_log_json_response(tx: &mut SNMPTransaction, jsb: &mut JsonBuilder) -> bool { snmp_log_response(jsb, tx).is_ok() } diff --git a/rust/src/ssh/logger.rs b/rust/src/ssh/logger.rs index 9bc7d7c33f39..008c6cb4517a 100644 --- a/rust/src/ssh/logger.rs +++ b/rust/src/ssh/logger.rs @@ -19,6 +19,7 @@ use super::ssh::SSHTransaction; use crate::jsonbuilder::{JsonBuilder, JsonError}; fn log_ssh(tx: &SSHTransaction, js: &mut JsonBuilder) -> Result { + js.open_object("ssh")?; if tx.cli_hdr.protover.is_empty() && tx.srv_hdr.protover.is_empty() { return Ok(false); } @@ -58,6 +59,7 @@ fn log_ssh(tx: &SSHTransaction, js: &mut JsonBuilder) -> Result } js.close()?; } + js.close()?; return Ok(true); } diff --git a/rust/src/tftp/log.rs b/rust/src/tftp/log.rs index b4837036a156..f6e63531a07e 100644 --- a/rust/src/tftp/log.rs +++ b/rust/src/tftp/log.rs @@ -24,6 +24,7 @@ fn tftp_log_request(tx: &mut TFTPTransaction, jb: &mut JsonBuilder) -> Result<(), JsonError> { + jb.open_object("tftp")?; match tx.opcode { 1 => jb.set_string("packet", "read")?, 2 => jb.set_string("packet", "write")?, @@ -31,6 +32,7 @@ fn tftp_log_request(tx: &mut TFTPTransaction, }; jb.set_string("file", tx.filename.as_str())?; jb.set_string("mode", tx.mode.as_str())?; + jb.close()?; Ok(()) } diff --git a/scripts/setup-app-layer.py b/scripts/setup-app-layer.py index 72f28c986c66..d8426634bca8 100755 --- a/scripts/setup-app-layer.py +++ b/scripts/setup-app-layer.py @@ -200,6 +200,10 @@ def logger_patch_output_c(proto): output = io.StringIO() inlines = open(filename).readlines() for i, line in enumerate(inlines): + if line.find("ALPROTO_TEMPLATE") > -1: + new_line = line.replace("TEMPLATE", proto.upper()).replace( + "template", proto.lower()) + output.write(new_line) if line.find("output-json-template.h") > -1: output.write(line.replace("template", proto.lower())) if line.find("/* Template JSON logger.") > -1: diff --git a/src/Makefile.am b/src/Makefile.am index a125e2a432ba..f8033de41b88 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -250,6 +250,7 @@ noinst_HEADERS = \ detect-mqtt-connect-clientid.h \ detect-mqtt-connect-flags.h \ detect-mqtt-connect-password.h \ + detect-mqtt-connect-protocol-string.h \ detect-mqtt-connect-username.h \ detect-mqtt-connect-willmessage.h \ detect-mqtt-connect-willtopic.h \ @@ -862,6 +863,7 @@ libsuricata_c_a_SOURCES = \ detect-mqtt-connect-clientid.c \ detect-mqtt-connect-flags.c \ detect-mqtt-connect-password.c \ + detect-mqtt-connect-protocol-string.c \ detect-mqtt-connect-username.c \ detect-mqtt-connect-willmessage.c \ detect-mqtt-connect-willtopic.c \ diff --git a/src/app-layer-ftp.c b/src/app-layer-ftp.c index c0a815e31dae..f46a4a967e24 100644 --- a/src/app-layer-ftp.c +++ b/src/app-layer-ftp.c @@ -1405,13 +1405,10 @@ uint16_t JsonGetNextLineFromBuffer(const char *buffer, const uint16_t len) return c == NULL ? len : (uint16_t)(c - buffer + 1); } -void EveFTPDataAddMetadata(const Flow *f, JsonBuilder *jb) +bool EveFTPDataAddMetadata(void *vtx, JsonBuilder *jb) { - const FtpDataState *ftp_state = NULL; - if (f->alstate == NULL) - return; - - ftp_state = (FtpDataState *)f->alstate; + const FtpDataState *ftp_state = (FtpDataState *)vtx; + jb_open_object(jb, "ftp_data"); if (ftp_state->file_name) { jb_set_string_from_bytes(jb, "filename", ftp_state->file_name, ftp_state->file_len); @@ -1426,6 +1423,8 @@ void EveFTPDataAddMetadata(const Flow *f, JsonBuilder *jb) default: break; } + jb_close(jb); + return true; } /** diff --git a/src/app-layer-ftp.h b/src/app-layer-ftp.h index f79c5c9e7675..fb71d6b52de7 100644 --- a/src/app-layer-ftp.h +++ b/src/app-layer-ftp.h @@ -190,7 +190,7 @@ uint64_t FTPMemuseGlobalCounter(void); uint64_t FTPMemcapGlobalCounter(void); uint16_t JsonGetNextLineFromBuffer(const char *buffer, const uint16_t len); -void EveFTPDataAddMetadata(const Flow *f, JsonBuilder *jb); +bool EveFTPDataAddMetadata(void *vtx, JsonBuilder *jb); #endif /* __APP_LAYER_FTP_H__ */ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index af247a1b4fa7..bd8f66519683 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -219,6 +219,7 @@ #include "detect-mqtt-connect-clientid.h" #include "detect-mqtt-connect-username.h" #include "detect-mqtt-connect-password.h" +#include "detect-mqtt-connect-protocol-string.h" #include "detect-mqtt-connect-willtopic.h" #include "detect-mqtt-connect-willmessage.h" #include "detect-mqtt-connack-sessionpresent.h" @@ -677,6 +678,7 @@ void SigTableSetup(void) DetectMQTTConnectClientIDRegister(); DetectMQTTConnectUsernameRegister(); DetectMQTTConnectPasswordRegister(); + DetectMQTTConnectProtocolStringRegister(); DetectMQTTConnectWillTopicRegister(); DetectMQTTConnectWillMessageRegister(); DetectMQTTConnackSessionPresentRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 92acd84f044b..abc1a403dd09 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -299,6 +299,7 @@ enum DetectKeywordId { DETECT_AL_MQTT_CONNECT_CLIENTID, DETECT_AL_MQTT_CONNECT_USERNAME, DETECT_AL_MQTT_CONNECT_PASSWORD, + DETECT_AL_MQTT_CONNECT_PROTOCOL_STRING, DETECT_AL_MQTT_CONNECT_WILLTOPIC, DETECT_AL_MQTT_CONNECT_WILLMESSAGE, DETECT_AL_MQTT_CONNACK_SESSION_PRESENT, diff --git a/src/detect-mqtt-connect-protocol-string.c b/src/detect-mqtt-connect-protocol-string.c new file mode 100644 index 000000000000..421b293845b7 --- /dev/null +++ b/src/detect-mqtt-connect-protocol-string.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2023 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. + */ + +/** + * + * \author Sascha Steinbiss + * + * Implements the mqtt.connect.protocolstring sticky buffer + */ + +#include "suricata-common.h" +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-mpm.h" +#include "detect-engine-prefilter.h" +#include "detect-mqtt-connect-protocol-string.h" +#include "rust.h" + +#define KEYWORD_NAME "mqtt.connect.protocol_string" +#define KEYWORD_DOC "mqtt-keywords.html#mqtt-connect-protocol_string" +#define BUFFER_NAME "mqtt.connect.protocol_string" +#define BUFFER_DESC "MQTT CONNECT protocol string" +static int g_buffer_id = 0; + +static int DetectMQTTConnectProtocolStringSetup( + DetectEngineCtx *de_ctx, Signature *s, const char *arg) +{ + if (DetectBufferSetActiveList(de_ctx, s, g_buffer_id) < 0) + return -1; + + if (DetectSignatureSetAppProto(s, ALPROTO_MQTT) < 0) + return -1; + + return 0; +} + +static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, + const int list_id) +{ + InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); + if (buffer->inspect == NULL) { + const uint8_t *b = NULL; + uint32_t b_len = 0; + + if (rs_mqtt_tx_get_connect_protocol_string(txv, &b, &b_len) != 1) + return NULL; + if (b == NULL || b_len == 0) + return NULL; + + InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); + InspectionBufferApplyTransforms(buffer, transforms); + } + return buffer; +} + +void DetectMQTTConnectProtocolStringRegister(void) +{ + /* mqtt.connect.protocol_string sticky buffer */ + sigmatch_table[DETECT_AL_MQTT_CONNECT_PROTOCOL_STRING].name = KEYWORD_NAME; + sigmatch_table[DETECT_AL_MQTT_CONNECT_PROTOCOL_STRING].desc = + "sticky buffer to match on the MQTT CONNECT protocol string"; + sigmatch_table[DETECT_AL_MQTT_CONNECT_PROTOCOL_STRING].url = "/rules/" KEYWORD_DOC; + sigmatch_table[DETECT_AL_MQTT_CONNECT_PROTOCOL_STRING].Setup = + DetectMQTTConnectProtocolStringSetup; + sigmatch_table[DETECT_AL_MQTT_CONNECT_PROTOCOL_STRING].flags |= SIGMATCH_NOOPT; + + DetectAppLayerInspectEngineRegister2(BUFFER_NAME, ALPROTO_MQTT, SIG_FLAG_TOSERVER, 0, + DetectEngineInspectBufferGeneric, GetData); + + DetectAppLayerMpmRegister2(BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, + GetData, ALPROTO_MQTT, 1); + + DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC); + + g_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME); + + SCLogDebug("registering " BUFFER_NAME " rule option"); +} diff --git a/src/detect-mqtt-connect-protocol-string.h b/src/detect-mqtt-connect-protocol-string.h new file mode 100644 index 000000000000..3bbb21ab10d4 --- /dev/null +++ b/src/detect-mqtt-connect-protocol-string.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2023 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 Sascha Steinbiss + */ + +#ifndef __DETECT_MQTT_CONNECT_PROTOCOLSTRING_H__ +#define __DETECT_MQTT_CONNECT_PROTOCOLSTRING_H__ + +void DetectMQTTConnectProtocolStringRegister(void); + +#endif /* __DETECT_MQTT_CONNECT_PROTOCOLSTRING_H__ */ diff --git a/src/detect-mqtt-type.c b/src/detect-mqtt-type.c index 3bc7f1e4f593..c55938c78c2d 100644 --- a/src/detect-mqtt-type.c +++ b/src/detect-mqtt-type.c @@ -43,7 +43,7 @@ void MQTTTypeRegisterTests(void); void DetectMQTTTypeFree(DetectEngineCtx *de_ctx, void *); /** - * \brief Registration function for ipopts: keyword + * \brief Registration function for mqtt.type: keyword */ void DetectMQTTTypeRegister (void) { diff --git a/src/output-json-alert.c b/src/output-json-alert.c index a7df1065509e..aa066619bd8d 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -137,164 +137,6 @@ static int AlertJsonDumpStreamSegmentCallback( return 1; } -static void AlertJsonTls(const Flow *f, JsonBuilder *js) -{ - SSLState *ssl_state = (SSLState *)FlowGetAppState(f); - if (ssl_state) { - jb_open_object(js, "tls"); - - JsonTlsLogJSONExtended(js, ssl_state); - - jb_close(js); - } - - return; -} - -static void AlertJsonSsh(const Flow *f, JsonBuilder *js) -{ - void *ssh_state = FlowGetAppState(f); - if (ssh_state) { - JsonBuilderMark mark = { 0, 0, 0 }; - void *tx_ptr = rs_ssh_state_get_tx(ssh_state, 0); - jb_get_mark(js, &mark); - jb_open_object(js, "ssh"); - if (rs_ssh_log_json(tx_ptr, js)) { - jb_close(js); - } else { - jb_restore_mark(js, &mark); - } - } - - return; -} - -static void AlertJsonHttp2(const Flow *f, const uint64_t tx_id, JsonBuilder *js) -{ - void *h2_state = FlowGetAppState(f); - if (h2_state) { - void *tx_ptr = rs_http2_state_get_tx(h2_state, tx_id); - if (tx_ptr) { - JsonBuilderMark mark = { 0, 0, 0 }; - jb_get_mark(js, &mark); - jb_open_object(js, "http"); - if (rs_http2_log_json(tx_ptr, js)) { - jb_close(js); - } else { - jb_restore_mark(js, &mark); - } - } - } - - return; -} - -static void AlertJsonDnp3(const Flow *f, const uint64_t tx_id, JsonBuilder *js) -{ - DNP3State *dnp3_state = (DNP3State *)FlowGetAppState(f); - if (dnp3_state) { - DNP3Transaction *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_DNP3, - dnp3_state, tx_id); - if (tx) { - JsonBuilderMark mark = { 0, 0, 0 }; - jb_get_mark(js, &mark); - bool logged = false; - jb_open_object(js, "dnp3"); - if (tx->is_request && tx->done) { - jb_open_object(js, "request"); - JsonDNP3LogRequest(js, tx); - jb_close(js); - logged = true; - } - if (!tx->is_request && tx->done) { - jb_open_object(js, "response"); - JsonDNP3LogResponse(js, tx); - jb_close(js); - logged = true; - } - if (logged) { - /* Close dnp3 object. */ - jb_close(js); - } else { - jb_restore_mark(js, &mark); - } - } - } -} - -static void AlertJsonDns(const Flow *f, const uint64_t tx_id, JsonBuilder *js) -{ - void *dns_state = (void *)FlowGetAppState(f); - if (dns_state) { - void *txptr = AppLayerParserGetTx(f->proto, ALPROTO_DNS, - dns_state, tx_id); - if (txptr) { - jb_open_object(js, "dns"); - JsonBuilder *qjs = JsonDNSLogQuery(txptr); - if (qjs != NULL) { - jb_set_object(js, "query", qjs); - jb_free(qjs); - } - JsonBuilder *ajs = JsonDNSLogAnswer(txptr); - if (ajs != NULL) { - jb_set_object(js, "answer", ajs); - jb_free(ajs); - } - jb_close(js); - } - } - return; -} - -static void AlertJsonSNMP(const Flow *f, const uint64_t tx_id, JsonBuilder *js) -{ - void *snmp_state = (void *)FlowGetAppState(f); - if (snmp_state != NULL) { - void *tx = AppLayerParserGetTx(f->proto, ALPROTO_SNMP, snmp_state, - tx_id); - if (tx != NULL) { - jb_open_object(js, "snmp"); - rs_snmp_log_json_response(js, tx); - jb_close(js); - } - } -} - -static void AlertJsonRDP(const Flow *f, const uint64_t tx_id, JsonBuilder *js) -{ - void *rdp_state = (void *)FlowGetAppState(f); - if (rdp_state != NULL) { - void *tx = AppLayerParserGetTx(f->proto, ALPROTO_RDP, rdp_state, - tx_id); - if (tx != NULL) { - JsonBuilderMark mark = { 0, 0, 0 }; - jb_get_mark(js, &mark); - if (!rs_rdp_to_json(tx, js)) { - jb_restore_mark(js, &mark); - } - } - } -} - -static void AlertJsonBitTorrentDHT(const Flow *f, const uint64_t tx_id, JsonBuilder *js) -{ - void *bittorrent_dht_state = (void *)FlowGetAppState(f); - if (bittorrent_dht_state != NULL) { - void *tx = - AppLayerParserGetTx(f->proto, ALPROTO_BITTORRENT_DHT, bittorrent_dht_state, tx_id); - if (tx != NULL) { - JsonBuilderMark mark = { 0, 0, 0 }; - jb_get_mark(js, &mark); - jb_open_object(js, "bittorrent_dht"); - if (rs_bittorrent_dht_logger_log(tx, js)) { - jb_close(js); - } else { - jb_restore_mark(js, &mark); - } - } - } -} - static void AlertJsonSourceTarget(const Packet *p, const PacketAlert *pa, JsonBuilder *js, JsonAddrInfo *addr) { @@ -471,7 +313,21 @@ static void AlertAddAppLayer(const Packet *p, JsonBuilder *jb, const uint64_t tx_id, const uint16_t option_flags) { const AppProto proto = FlowGetAppProtocol(p->flow); + SimpleJsonAppLayerLogger *al = GetAppProtoSimpleJsonLogger(proto); JsonBuilderMark mark = { 0, 0, 0 }; + if (al && al->LogTx) { + void *state = FlowGetAppState(p->flow); + if (state) { + void *tx = AppLayerParserGetTx(p->flow->proto, proto, state, tx_id); + if (tx) { + jb_get_mark(jb, &mark); + if (!al->LogTx(tx, jb)) { + jb_restore_mark(jb, &mark); + } + } + } + return; + } switch (proto) { case ALPROTO_HTTP1: // TODO: Could result in an empty http object being logged. @@ -486,12 +342,6 @@ static void AlertAddAppLayer(const Packet *p, JsonBuilder *jb, } jb_close(jb); break; - case ALPROTO_TLS: - AlertJsonTls(p->flow, jb); - break; - case ALPROTO_SSH: - AlertJsonSsh(p->flow, jb); - break; case ALPROTO_SMTP: jb_get_mark(jb, &mark); jb_open_object(jb, "smtp"); @@ -535,63 +385,12 @@ static void AlertAddAppLayer(const Packet *p, JsonBuilder *jb, jb_restore_mark(jb, &mark); } break; - case ALPROTO_SIP: - JsonSIPAddMetadata(jb, p->flow, tx_id); - break; - case ALPROTO_RFB: - jb_get_mark(jb, &mark); - if (!JsonRFBAddMetadata(p->flow, tx_id, jb)) { - jb_restore_mark(jb, &mark); - } - break; - case ALPROTO_FTPDATA: - jb_get_mark(jb, &mark); - jb_open_object(jb, "ftp_data"); - EveFTPDataAddMetadata(p->flow, jb); - jb_close(jb); - break; - case ALPROTO_DNP3: - AlertJsonDnp3(p->flow, tx_id, jb); - break; - case ALPROTO_HTTP2: - AlertJsonHttp2(p->flow, tx_id, jb); - break; - case ALPROTO_DNS: - AlertJsonDns(p->flow, tx_id, jb); - break; case ALPROTO_IKE: jb_get_mark(jb, &mark); if (!EveIKEAddMetadata(p->flow, tx_id, jb)) { jb_restore_mark(jb, &mark); } break; - case ALPROTO_MQTT: - jb_get_mark(jb, &mark); - if (!JsonMQTTAddMetadata(p->flow, tx_id, jb)) { - jb_restore_mark(jb, &mark); - } - break; - case ALPROTO_QUIC: - jb_get_mark(jb, &mark); - if (!JsonQuicAddMetadata(p->flow, tx_id, jb)) { - jb_restore_mark(jb, &mark); - } - break; - case ALPROTO_SNMP: - AlertJsonSNMP(p->flow, tx_id, jb); - break; - case ALPROTO_RDP: - AlertJsonRDP(p->flow, tx_id, jb); - break; - case ALPROTO_MODBUS: - jb_get_mark(jb, &mark); - if (!JsonModbusAddMetadata(p->flow, tx_id, jb)) { - jb_restore_mark(jb, &mark); - } - break; - case ALPROTO_BITTORRENT_DHT: - AlertJsonBitTorrentDHT(p->flow, tx_id, jb); - break; default: break; } diff --git a/src/output-json-bittorrent-dht.c b/src/output-json-bittorrent-dht.c index 08b7dc4d722c..066df78f61fb 100644 --- a/src/output-json-bittorrent-dht.c +++ b/src/output-json-bittorrent-dht.c @@ -65,11 +65,9 @@ static int JsonBitTorrentDHTLogger(ThreadVars *tv, void *thread_data, const Pack return TM_ECODE_FAILED; } - jb_open_object(js, "bittorrent_dht"); if (!rs_bittorrent_dht_logger_log(tx, js)) { goto error; } - jb_close(js); OutputJsonBuilderBuffer(js, thread->ctx); jb_free(js); diff --git a/src/output-json-dnp3.c b/src/output-json-dnp3.c index 97b1e92e00ce..4336e04e070c 100644 --- a/src/output-json-dnp3.c +++ b/src/output-json-dnp3.c @@ -210,6 +210,27 @@ void JsonDNP3LogResponse(JsonBuilder *js, DNP3Transaction *dnp3tx) jb_close(js); } +bool AlertJsonDnp3(void *vtx, JsonBuilder *js) +{ + DNP3Transaction *tx = (DNP3Transaction *)vtx; + bool logged = false; + jb_open_object(js, "dnp3"); + if (tx->is_request && tx->done) { + jb_open_object(js, "request"); + JsonDNP3LogRequest(js, tx); + jb_close(js); + logged = true; + } + if (!tx->is_request && tx->done) { + jb_open_object(js, "response"); + JsonDNP3LogResponse(js, tx); + jb_close(js); + logged = true; + } + jb_close(js); + return logged; +} + static int JsonDNP3LoggerToServer(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id) { diff --git a/src/output-json-dnp3.h b/src/output-json-dnp3.h index 85d02ff1011c..6f81026780d9 100644 --- a/src/output-json-dnp3.h +++ b/src/output-json-dnp3.h @@ -24,5 +24,6 @@ void JsonDNP3LogRequest(JsonBuilder *js, DNP3Transaction *); void JsonDNP3LogResponse(JsonBuilder *js, DNP3Transaction *); void JsonDNP3LogRegister(void); +bool AlertJsonDnp3(void *vtx, JsonBuilder *js); #endif /* __OUTPUT_JSON_DNP3_H__ */ diff --git a/src/output-json-dns.c b/src/output-json-dns.c index 020e27853a9e..b27c67feb240 100644 --- a/src/output-json-dns.c +++ b/src/output-json-dns.c @@ -263,7 +263,7 @@ typedef struct LogDnsLogThread_ { OutputJsonThreadCtx *ctx; } LogDnsLogThread; -JsonBuilder *JsonDNSLogQuery(void *txptr) +static JsonBuilder *JsonDNSLogQuery(void *txptr) { JsonBuilder *queryjb = jb_new_array(); if (queryjb == NULL) { @@ -292,7 +292,7 @@ JsonBuilder *JsonDNSLogQuery(void *txptr) return queryjb; } -JsonBuilder *JsonDNSLogAnswer(void *txptr) +static JsonBuilder *JsonDNSLogAnswer(void *txptr) { if (!rs_dns_do_log_answer(txptr, LOG_ALL_RRTYPES)) { return NULL; @@ -304,6 +304,23 @@ JsonBuilder *JsonDNSLogAnswer(void *txptr) } } +bool AlertJsonDns(void *txptr, JsonBuilder *js) +{ + jb_open_object(js, "dns"); + JsonBuilder *qjs = JsonDNSLogQuery(txptr); + if (qjs != NULL) { + jb_set_object(js, "query", qjs); + jb_free(qjs); + } + JsonBuilder *ajs = JsonDNSLogAnswer(txptr); + if (ajs != NULL) { + jb_set_object(js, "answer", ajs); + jb_free(ajs); + } + jb_close(js); + return true; +} + static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id) { diff --git a/src/output-json-dns.h b/src/output-json-dns.h index 1e19427361b8..f46cad011089 100644 --- a/src/output-json-dns.h +++ b/src/output-json-dns.h @@ -26,7 +26,6 @@ void JsonDnsLogRegister(void); -JsonBuilder *JsonDNSLogQuery(void *txptr) __attribute__((nonnull)); -JsonBuilder *JsonDNSLogAnswer(void *txptr) __attribute__((nonnull)); +bool AlertJsonDns(void *vtx, JsonBuilder *js); #endif /* __OUTPUT_JSON_DNS_H__ */ diff --git a/src/output-json-file.c b/src/output-json-file.c index 3b015ea88e06..f81fc0908ebc 100644 --- a/src/output-json-file.c +++ b/src/output-json-file.c @@ -123,6 +123,7 @@ JsonBuilder *JsonBuildFileInfoRecord(const Packet *p, const File *ff, void *tx, return NULL; JsonBuilderMark mark = { 0, 0, 0 }; + SimpleJsonAppLayerLogger *al; switch (p->flow->alproto) { case ALPROTO_HTTP1: jb_open_object(js, "http"); @@ -172,13 +173,19 @@ JsonBuilder *JsonBuildFileInfoRecord(const Packet *p, const File *ff, void *tx, jb_restore_mark(js, &mark); } break; - case ALPROTO_HTTP2: - jb_get_mark(js, &mark); - jb_open_object(js, "http"); - if (EveHTTP2AddMetadata(p->flow, tx_id, js)) { - jb_close(js); - } else { - jb_restore_mark(js, &mark); + default: + al = GetAppProtoSimpleJsonLogger(p->flow->alproto); + if (al && al->LogTx) { + void *state = FlowGetAppState(p->flow); + if (state) { + tx = AppLayerParserGetTx(p->flow->proto, p->flow->alproto, state, tx_id); + if (tx) { + jb_get_mark(js, &mark); + if (!al->LogTx(tx, js)) { + jb_restore_mark(js, &mark); + } + } + } } break; } diff --git a/src/output-json-ftp.c b/src/output-json-ftp.c index ece9344bf409..34422f72f4af 100644 --- a/src/output-json-ftp.c +++ b/src/output-json-ftp.c @@ -46,17 +46,19 @@ #include "app-layer-ftp.h" #include "output-json-ftp.h" -static void EveFTPLogCommand(FTPTransaction *tx, JsonBuilder *jb) +bool EveFTPLogCommand(void *vtx, JsonBuilder *jb) { + FTPTransaction *tx = vtx; /* Preallocate array objects to simplify failure case */ JsonBuilder *js_resplist = NULL; if (!TAILQ_EMPTY(&tx->response_list)) { js_resplist = jb_new_array(); if (unlikely(js_resplist == NULL)) { - return; + return false; } } + jb_open_object(jb, "ftp"); jb_set_string(jb, "command", tx->command_descriptor->command_name); uint32_t min_length = tx->command_descriptor->command_length + 1; /* command + space */ if (tx->request_length > min_length) { @@ -149,6 +151,8 @@ static void EveFTPLogCommand(FTPTransaction *tx, JsonBuilder *jb) } else { JB_SET_FALSE(jb, "reply_truncated"); } + jb_close(jb); + return true; } @@ -164,20 +168,16 @@ static int JsonFTPLogger(ThreadVars *tv, void *thread_data, } else { event_type = "ftp"; } - FTPTransaction *tx = vtx; JsonBuilder *jb = CreateEveHeaderWithTxId(p, LOG_DIR_FLOW, event_type, NULL, tx_id, thread->ctx); if (likely(jb)) { - jb_open_object(jb, event_type); if (f->alproto == ALPROTO_FTPDATA) { - EveFTPDataAddMetadata(f, jb); + if (!EveFTPDataAddMetadata(vtx, jb)) { + goto fail; + } } else { - EveFTPLogCommand(tx, jb); - } - - if (!jb_close(jb)) { - goto fail; + EveFTPLogCommand(vtx, jb); } OutputJsonBuilderBuffer(jb, thread); diff --git a/src/output-json-ftp.h b/src/output-json-ftp.h index acba5539e1c6..704defd9585c 100644 --- a/src/output-json-ftp.h +++ b/src/output-json-ftp.h @@ -25,5 +25,6 @@ #define __OUTPUT_JSON_FTP_H__ void JsonFTPLogRegister(void); +bool EveFTPLogCommand(void *vtx, JsonBuilder *js); #endif /* __OUTPUT_JSON_FTP_H__ */ diff --git a/src/output-json-http2.c b/src/output-json-http2.c index d762e76d0665..7165ae8f6302 100644 --- a/src/output-json-http2.c +++ b/src/output-json-http2.c @@ -61,19 +61,6 @@ typedef struct JsonHttp2LogThread_ { OutputJsonThreadCtx *ctx; } JsonHttp2LogThread; - -bool EveHTTP2AddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *jb) -{ - void *state = FlowGetAppState(f); - if (state) { - void *tx = AppLayerParserGetTx(f->proto, ALPROTO_HTTP2, state, tx_id); - if (tx) { - return rs_http2_log_json(tx, jb); - } - } - return false; -} - static int JsonHttp2Logger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *txptr, uint64_t tx_id) { @@ -88,11 +75,9 @@ static int JsonHttp2Logger(ThreadVars *tv, void *thread_data, const Packet *p, if (unlikely(js == NULL)) return 0; - jb_open_object(js, "http"); if (!rs_http2_log_json(txptr, js)) { goto end; } - jb_close(js); OutputJsonBuilderBuffer(js, aft->ctx); end: jb_free(js); diff --git a/src/output-json-http2.h b/src/output-json-http2.h index 66bf2ade968e..88ba420ab2df 100644 --- a/src/output-json-http2.h +++ b/src/output-json-http2.h @@ -25,6 +25,5 @@ #define __OUTPUT_JSON_HTTP2_H__ void JsonHttp2LogRegister(void); -bool EveHTTP2AddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *jb); #endif /* __OUTPUT_JSON_HTTP2_H__ */ diff --git a/src/output-json-krb5.c b/src/output-json-krb5.c index 5e6fbad5ecd1..9fc45c5d3c53 100644 --- a/src/output-json-krb5.c +++ b/src/output-json-krb5.c @@ -59,11 +59,9 @@ static int JsonKRB5Logger(ThreadVars *tv, void *thread_data, return TM_ECODE_FAILED; } - jb_open_object(jb, "krb5"); - if (!rs_krb5_log_json_response(jb, krb5tx)) { + if (!rs_krb5_log_json_response(krb5tx, jb)) { goto error; } - jb_close(jb); OutputJsonBuilderBuffer(jb, thread); diff --git a/src/output-json-modbus.c b/src/output-json-modbus.c index ace8c061f92d..9e508ead9acc 100644 --- a/src/output-json-modbus.c +++ b/src/output-json-modbus.c @@ -136,19 +136,6 @@ static TmEcode JsonModbusLogThreadDeinit(ThreadVars *t, void *data) return TM_ECODE_OK; } -bool JsonModbusAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js) -{ - void *state = FlowGetAppState(f); - if (state) { - void *tx = AppLayerParserGetTx(f->proto, ALPROTO_MODBUS, state, tx_id); - if (tx) { - return rs_modbus_to_json(tx, js); - } - } - - return false; -} - void JsonModbusLogRegister(void) { /* Register as an eve sub-module. */ diff --git a/src/output-json-modbus.h b/src/output-json-modbus.h index 9bde2dae57a3..2b07e4eb2d5c 100644 --- a/src/output-json-modbus.h +++ b/src/output-json-modbus.h @@ -19,6 +19,5 @@ #define __OUTPUT_JSON_MODBUS_H__ void JsonModbusLogRegister(void); -bool JsonModbusAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js); #endif /* __OUTPUT_JSON_MODBUS_H__ */ diff --git a/src/output-json-mqtt.c b/src/output-json-mqtt.c index 9ea890508070..2f600343e20d 100644 --- a/src/output-json-mqtt.c +++ b/src/output-json-mqtt.c @@ -59,17 +59,9 @@ typedef struct LogMQTTLogThread_ { OutputJsonThreadCtx *ctx; } LogMQTTLogThread; -bool JsonMQTTAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js) +bool JsonMQTTAddMetadata(void *vtx, JsonBuilder *js) { - MQTTState *state = FlowGetAppState(f); - if (state) { - MQTTTransaction *tx = AppLayerParserGetTx(f->proto, ALPROTO_MQTT, state, tx_id); - if (tx) { - return rs_mqtt_logger_log(tx, MQTT_DEFAULTS, js); - } - } - - return false; + return rs_mqtt_logger_log(vtx, MQTT_DEFAULTS, js); } static int JsonMQTTLogger(ThreadVars *tv, void *thread_data, diff --git a/src/output-json-mqtt.h b/src/output-json-mqtt.h index 1acb4e107faf..42d66f48680d 100644 --- a/src/output-json-mqtt.h +++ b/src/output-json-mqtt.h @@ -25,6 +25,6 @@ #define __OUTPUT_JSON_MQTT_H__ void JsonMQTTLogRegister(void); -bool JsonMQTTAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js); +bool JsonMQTTAddMetadata(void *vtx, JsonBuilder *js); #endif /* __OUTPUT_JSON_MQTT_H__ */ diff --git a/src/output-json-quic.c b/src/output-json-quic.c index fdf2d0f09340..830ac78fdfbb 100644 --- a/src/output-json-quic.c +++ b/src/output-json-quic.c @@ -140,19 +140,6 @@ static TmEcode JsonQuicLogThreadDeinit(ThreadVars *t, void *data) return TM_ECODE_OK; } -bool JsonQuicAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js) -{ - void *state = FlowGetAppState(f); - if (state) { - void *tx = AppLayerParserGetTx(f->proto, ALPROTO_QUIC, state, tx_id); - if (tx) { - return rs_quic_to_json(tx, js); - } - } - - return false; -} - void JsonQuicLogRegister(void) { /* Register as an eve sub-module. */ diff --git a/src/output-json-quic.h b/src/output-json-quic.h index 2448d5063a34..48e38185f2bd 100644 --- a/src/output-json-quic.h +++ b/src/output-json-quic.h @@ -22,7 +22,6 @@ #ifndef __OUTPUT_JSON_QUIC_H__ #define __OUTPUT_JSON_QUIC_H__ -bool JsonQuicAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js); void JsonQuicLogRegister(void); #endif /* __OUTPUT_JSON_QUIC_H__ */ diff --git a/src/output-json-rfb.c b/src/output-json-rfb.c index cc12d2f1bbdc..e2b832bece13 100644 --- a/src/output-json-rfb.c +++ b/src/output-json-rfb.c @@ -46,19 +46,6 @@ #include "rust-bindings.h" -bool JsonRFBAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js) -{ - void *state = FlowGetAppState(f); - if (state) { - RFBTransaction *tx = AppLayerParserGetTx(f->proto, ALPROTO_RFB, state, tx_id); - if (tx) { - return rs_rfb_logger_log(tx, js); - } - } - - return false; -} - static int JsonRFBLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) { diff --git a/src/output-json-rfb.h b/src/output-json-rfb.h index 1264ee3f6b4b..7e4e48ebd4c8 100644 --- a/src/output-json-rfb.h +++ b/src/output-json-rfb.h @@ -26,6 +26,4 @@ void JsonRFBLogRegister(void); -bool JsonRFBAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js); - #endif /* __OUTPUT_JSON_RFB_H__ */ diff --git a/src/output-json-sip.c b/src/output-json-sip.c index 8297be1cc3eb..7dd442cf6aba 100644 --- a/src/output-json-sip.c +++ b/src/output-json-sip.c @@ -48,17 +48,6 @@ #include "rust.h" -void JsonSIPAddMetadata(JsonBuilder *js, const Flow *f, uint64_t tx_id) -{ - SIPState *state = FlowGetAppState(f); - if (state) { - SIPTransaction *tx = AppLayerParserGetTx(f->proto, ALPROTO_SIP, state, tx_id); - if (tx) { - rs_sip_log_json(tx, js); - } - } -} - static int JsonSIPLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) { diff --git a/src/output-json-sip.h b/src/output-json-sip.h index 60145dab5b98..0d2c53fa50df 100644 --- a/src/output-json-sip.h +++ b/src/output-json-sip.h @@ -26,6 +26,4 @@ void JsonSIPLogRegister(void); -void JsonSIPAddMetadata(JsonBuilder *js, const Flow *f, uint64_t tx_id); - #endif /* __OUTPUT_JSON_SIP_H__ */ diff --git a/src/output-json-snmp.c b/src/output-json-snmp.c index 27545b6f6903..cbf0a7c992e4 100644 --- a/src/output-json-snmp.c +++ b/src/output-json-snmp.c @@ -59,11 +59,9 @@ static int JsonSNMPLogger(ThreadVars *tv, void *thread_data, return TM_ECODE_FAILED; } - jb_open_object(jb, "snmp"); - if (!rs_snmp_log_json_response(jb, snmptx)) { + if (!rs_snmp_log_json_response(snmptx, jb)) { goto error; } - jb_close(jb); OutputJsonBuilderBuffer(jb, thread); diff --git a/src/output-json-ssh.c b/src/output-json-ssh.c index 5ec70142f634..45a8d8eab333 100644 --- a/src/output-json-ssh.c +++ b/src/output-json-ssh.c @@ -64,11 +64,9 @@ static int JsonSshLogger(ThreadVars *tv, void *thread_data, const Packet *p, if (unlikely(js == NULL)) return 0; - jb_open_object(js, "ssh"); if (!rs_ssh_log_json(txptr, js)) { goto end; } - jb_close(js); OutputJsonBuilderBuffer(js, thread); end: diff --git a/src/output-json-template.c b/src/output-json-template.c index 76d42ad834e6..2ca48b7ae373 100644 --- a/src/output-json-template.c +++ b/src/output-json-template.c @@ -74,11 +74,9 @@ static int JsonTemplateLogger(ThreadVars *tv, void *thread_data, const Packet *p return TM_ECODE_FAILED; } - jb_open_object(js, "template"); if (!rs_template_logger_log(tx, js)) { goto error; } - jb_close(js); OutputJsonBuilderBuffer(js, thread->ctx); jb_free(js); diff --git a/src/output-json-tftp.c b/src/output-json-tftp.c index 4fff67a8b696..a0bc9ee1809e 100644 --- a/src/output-json-tftp.c +++ b/src/output-json-tftp.c @@ -58,11 +58,9 @@ static int JsonTFTPLogger(ThreadVars *tv, void *thread_data, return TM_ECODE_FAILED; } - jb_open_object(jb, "tftp"); if (unlikely(!rs_tftp_log_json_request(tx, jb))) { goto error; } - jb_close(jb); OutputJsonBuilderBuffer(jb, thread); diff --git a/src/output-json-tls.c b/src/output-json-tls.c index 9771f4d1cd7c..7460a32f2574 100644 --- a/src/output-json-tls.c +++ b/src/output-json-tls.c @@ -392,8 +392,9 @@ static void JsonTlsLogJSONCustom(OutputTlsCtx *tls_ctx, JsonBuilder *js, } } -void JsonTlsLogJSONExtended(JsonBuilder *tjs, SSLState * state) +static bool JsonTlsLogJSONExtendedAux(void *vtx, JsonBuilder *tjs) { + SSLState *state = (SSLState *)vtx; JsonTlsLogJSONBasic(tjs, state); /* tls serial */ @@ -425,6 +426,15 @@ void JsonTlsLogJSONExtended(JsonBuilder *tjs, SSLState * state) JsonTlsLogClientCert(tjs, &state->client_connp, false, false); jb_close(tjs); } + return true; +} + +bool JsonTlsLogJSONExtended(void *vtx, JsonBuilder *tjs) +{ + jb_open_object(tjs, "tls"); + bool r = JsonTlsLogJSONExtendedAux(vtx, tjs); + jb_close(tjs); + return r; } static int JsonTlsLogger(ThreadVars *tv, void *thread_data, const Packet *p, @@ -459,7 +469,7 @@ static int JsonTlsLogger(ThreadVars *tv, void *thread_data, const Packet *p, } /* log extended */ else if (tls_ctx->flags & LOG_TLS_EXTENDED) { - JsonTlsLogJSONExtended(js, ssl_state); + JsonTlsLogJSONExtendedAux(ssl_state, js); } /* log basic */ else { diff --git a/src/output-json-tls.h b/src/output-json-tls.h index 737e6233ef10..42f706b91d3f 100644 --- a/src/output-json-tls.h +++ b/src/output-json-tls.h @@ -29,6 +29,6 @@ void JsonTlsLogRegister(void); #include "app-layer-ssl.h" void JsonTlsLogJSONBasic(JsonBuilder *js, SSLState *ssl_state); -void JsonTlsLogJSONExtended(JsonBuilder *js, SSLState *ssl_state); +bool JsonTlsLogJSONExtended(void *vtx, JsonBuilder *js); #endif /* __OUTPUT_JSON_TLS_H__ */ diff --git a/src/output.c b/src/output.c index c13ab4862eda..087f2b9a18e3 100644 --- a/src/output.c +++ b/src/output.c @@ -67,6 +67,8 @@ #include "log-stats.h" #include "output-json-nfs.h" #include "output-json-ftp.h" +// for misplaced EveFTPDataAddMetadata +#include "app-layer-ftp.h" #include "output-json-tftp.h" #include "output-json-smb.h" #include "output-json-ike.h" @@ -1126,3 +1128,53 @@ void OutputRegisterLoggers(void) /* BitTorrent DHT JSON logger */ JsonBitTorrentDHTLogRegister(); } + +static SimpleJsonAppLayerLogger simple_json_applayer_loggers[ALPROTO_MAX] = { + { ALPROTO_UNKNOWN, NULL }, + { ALPROTO_HTTP1, NULL }, // special: uses some options flags + { ALPROTO_FTP, EveFTPLogCommand }, + { ALPROTO_SMTP, NULL }, // special: uses state + { ALPROTO_TLS, JsonTlsLogJSONExtended }, + { ALPROTO_SSH, rs_ssh_log_json }, + { ALPROTO_IMAP, NULL }, // protocol detection only + { ALPROTO_JABBER, NULL }, // no parser, no logging + { ALPROTO_SMB, NULL }, // special: uses state + { ALPROTO_DCERPC, NULL }, // TODO missing + { ALPROTO_IRC, NULL }, // no parser, no logging + { ALPROTO_DNS, AlertJsonDns }, + { ALPROTO_MODBUS, (SimpleJsonTxLogFunc)rs_modbus_to_json }, + { ALPROTO_ENIP, NULL }, // no logging + { ALPROTO_DNP3, AlertJsonDnp3 }, + { ALPROTO_NFS, NULL }, // special: uses state + { ALPROTO_NTP, NULL }, // no logging + { ALPROTO_FTPDATA, EveFTPDataAddMetadata }, + { ALPROTO_TFTP, (SimpleJsonTxLogFunc)rs_tftp_log_json_request }, + { ALPROTO_IKE, NULL }, // special: uses state + { ALPROTO_KRB5, (SimpleJsonTxLogFunc)rs_krb5_log_json_response }, + { ALPROTO_QUIC, rs_quic_to_json }, + { ALPROTO_DHCP, NULL }, // TODO missing + { ALPROTO_SNMP, (SimpleJsonTxLogFunc)rs_snmp_log_json_response }, + { ALPROTO_SIP, (SimpleJsonTxLogFunc)rs_sip_log_json }, + { ALPROTO_RFB, rs_rfb_logger_log }, + { ALPROTO_MQTT, JsonMQTTAddMetadata }, + { ALPROTO_PGSQL, NULL }, // TODO missing + { ALPROTO_TELNET, NULL }, // no logging + { ALPROTO_TEMPLATE, rs_template_logger_log }, + { ALPROTO_RDP, (SimpleJsonTxLogFunc)rs_rdp_to_json }, + { ALPROTO_HTTP2, rs_http2_log_json }, + { ALPROTO_BITTORRENT_DHT, rs_bittorrent_dht_logger_log }, + { ALPROTO_HTTP, NULL }, // signature protocol, not for app-layer logging + { ALPROTO_FAILED, NULL }, +#ifdef UNITTESTS + { ALPROTO_TEST, NULL }, +#endif /* UNITESTS */ +}; + +SimpleJsonAppLayerLogger *GetAppProtoSimpleJsonLogger(AppProto alproto) +{ + if (alproto < ALPROTO_MAX) { + BUG_ON(simple_json_applayer_loggers[alproto].proto != alproto); + return &simple_json_applayer_loggers[alproto]; + } + return NULL; +} diff --git a/src/output.h b/src/output.h index 5c2d7bc90e62..3daba06a33d3 100644 --- a/src/output.h +++ b/src/output.h @@ -208,4 +208,13 @@ void OutputLoggerExitPrintStats(ThreadVars *, void *); void OutputSetupActiveLoggers(void); void OutputClearActiveLoggers(void); +typedef bool (*SimpleJsonTxLogFunc)(void *, struct JsonBuilder *); + +typedef struct SimpleJsonAppLayerLogger { + AppProto proto; + SimpleJsonTxLogFunc LogTx; +} SimpleJsonAppLayerLogger; + +SimpleJsonAppLayerLogger *GetAppProtoSimpleJsonLogger(AppProto alproto); + #endif /* ! __OUTPUT_H__ */ diff --git a/src/util-memcmp.c b/src/util-memcmp.c index bf3d8e56b341..7113b82dd60c 100644 --- a/src/util-memcmp.c +++ b/src/util-memcmp.c @@ -38,10 +38,8 @@ static int MemcmpTest01 (void) uint8_t a[] = "abcd"; uint8_t b[] = "abcd"; - if (SCMemcmp(a, b, sizeof(a)-1) != 0) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 0); + PASS; } static int MemcmpTest02 (void) @@ -49,10 +47,8 @@ static int MemcmpTest02 (void) uint8_t a[] = "abcdabcdabcdabcd"; uint8_t b[] = "abcdabcdabcdabcd"; - if (SCMemcmp(a, b, sizeof(a)-1) != 0) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 0); + PASS; } static int MemcmpTest03 (void) @@ -60,10 +56,8 @@ static int MemcmpTest03 (void) uint8_t a[] = "abcdabcd"; uint8_t b[] = "abcdabcd"; - if (SCMemcmp(a, b, sizeof(a)-1) != 0) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 0); + PASS; } static int MemcmpTest04 (void) @@ -72,12 +66,9 @@ static int MemcmpTest04 (void) uint8_t b[] = "abcD"; int r = SCMemcmp(a, b, sizeof(a)-1); - if (r != 1) { - printf("%s != %s, but memcmp returned %d: ", a, b, r); - return 0; - } + FAIL_IF(r != 1); - return 1; + PASS; } static int MemcmpTest05 (void) @@ -85,10 +76,8 @@ static int MemcmpTest05 (void) uint8_t a[] = "abcdabcdabcdabcd"; uint8_t b[] = "abcDabcdabcdabcd"; - if (SCMemcmp(a, b, sizeof(a)-1) != 1) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 1); + PASS; } static int MemcmpTest06 (void) @@ -96,10 +85,8 @@ static int MemcmpTest06 (void) uint8_t a[] = "abcdabcd"; uint8_t b[] = "abcDabcd"; - if (SCMemcmp(a, b, sizeof(a)-1) != 1) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 1); + PASS; } static int MemcmpTest07 (void) @@ -107,10 +94,8 @@ static int MemcmpTest07 (void) uint8_t a[] = "abcd"; uint8_t b[] = "abcde"; - if (SCMemcmp(a, b, sizeof(a)-1) != 0) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 0); + PASS; } static int MemcmpTest08 (void) @@ -118,10 +103,8 @@ static int MemcmpTest08 (void) uint8_t a[] = "abcdabcdabcdabcd"; uint8_t b[] = "abcdabcdabcdabcde"; - if (SCMemcmp(a, b, sizeof(a)-1) != 0) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 0); + PASS; } static int MemcmpTest09 (void) @@ -129,10 +112,8 @@ static int MemcmpTest09 (void) uint8_t a[] = "abcdabcd"; uint8_t b[] = "abcdabcde"; - if (SCMemcmp(a, b, sizeof(a)-1) != 0) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 0); + PASS; } static int MemcmpTest10 (void) @@ -140,10 +121,8 @@ static int MemcmpTest10 (void) uint8_t a[] = "abcd"; uint8_t b[] = "Zbcde"; - if (SCMemcmp(a, b, sizeof(a)-1) != 1) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 1); + PASS; } static int MemcmpTest11 (void) @@ -151,10 +130,8 @@ static int MemcmpTest11 (void) uint8_t a[] = "abcdabcdabcdabcd"; uint8_t b[] = "Zbcdabcdabcdabcde"; - if (SCMemcmp(a, b, sizeof(a)-1) != 1) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 1); + PASS; } static int MemcmpTest12 (void) @@ -162,10 +139,8 @@ static int MemcmpTest12 (void) uint8_t a[] = "abcdabcd"; uint8_t b[] = "Zbcdabcde"; - if (SCMemcmp(a, b, sizeof(a)-1) != 1) - return 0; - - return 1; + FAIL_IF(SCMemcmp(a, b, sizeof(a) - 1) != 1); + PASS; } static int MemcmpTest13 (void) @@ -173,10 +148,8 @@ static int MemcmpTest13 (void) uint8_t a[] = "abcdefgh"; uint8_t b[] = "AbCdEfGhIjK"; - if (SCMemcmpLowercase(a, b, sizeof(a)-1) != 0) - return 0; - - return 1; + FAIL_IF(SCMemcmpLowercase(a, b, sizeof(a) - 1) != 0); + PASS; } #include "util-cpu.h" @@ -216,10 +189,9 @@ static int MemcmpTest14 (void) SCLogInfo("ticks passed %"PRIu64, ticks_end - ticks_start); printf("r1 %d\n", r1); - if (r1 != (51 * TEST_RUNS)) - return 0; + FAIL_IF(r1 != (51 * TEST_RUNS)); #endif - return 1; + PASS; } static int MemcmpTest15 (void) @@ -255,10 +227,9 @@ static int MemcmpTest15 (void) SCLogInfo("ticks passed %"PRIu64, ticks_end - ticks_start); printf("r2 %d\n", r2); - if (r2 != (51 * TEST_RUNS)) - return 0; + FAIL_IF(r2 != (51 * TEST_RUNS)); #endif - return 1; + PASS; } static int MemcmpTest16 (void) @@ -294,10 +265,9 @@ static int MemcmpTest16 (void) SCLogInfo("ticks passed %"PRIu64, ticks_end - ticks_start); printf("r3 %d\n", r3); - if (r3 != (51 * TEST_RUNS)) - return 0; + FAIL_IF(r3 != (51 * TEST_RUNS)); #endif - return 1; + PASS; } static int MemcmpTest17 (void) @@ -333,10 +303,9 @@ static int MemcmpTest17 (void) SCLogInfo("ticks passed %"PRIu64, ticks_end - ticks_start); printf("r4 %d\n", r4); - if (r4 != (51 * TEST_RUNS)) - return 0; + FAIL_IF(r4 != (51 * TEST_RUNS)); #endif - return 1; + PASS; } struct MemcmpTest18Tests { @@ -370,12 +339,11 @@ static int MemcmpTest18 (void) while (t && t->a != NULL) { - if (SCMemcmpLowercase(t->a, t->b, strlen(t->a)-1) != t->result) - return 0; + FAIL_IF(SCMemcmpLowercase(t->a, t->b, strlen(t->a) - 1) != t->result); t++; } - return 1; + PASS; } #endif /* UNITTESTS */