Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ftp/eve: Convert to JsonBuilder #5030

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 14 additions & 13 deletions src/app-layer-ftp.c
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2017 Open Information Security Foundation
/* Copyright (C) 2007-2020 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
Expand Down Expand Up @@ -1439,36 +1439,37 @@ uint16_t JsonGetNextLineFromBuffer(const char *buffer, const uint16_t len)
return c == NULL ? len : c - buffer + 1;
}

json_t *JsonFTPDataAddMetadata(const Flow *f)
void JsonFTPDataAddMetadata(const Flow *f, JsonBuilder *jb)
{
const FtpDataState *ftp_state = NULL;
if (f->alstate == NULL)
return NULL;
return;

ftp_state = (FtpDataState *)f->alstate;
json_t *ftpd = json_object();
if (ftpd == NULL)
return NULL;

jb_open_object(jb, "ftp-data");
if (ftp_state->file_name) {
size_t size = ftp_state->file_len * 2 + 1;
char string[size];
BytesToStringBuffer(ftp_state->file_name, ftp_state->file_len, string, size);
json_object_set_new(ftpd, "filename", SCJsonString(string));
char fname[size];
BytesToStringBuffer(ftp_state->file_name, ftp_state->file_len, fname, size);
jb_set_string(jb, "filename", fname);
}
switch (ftp_state->command) {
case FTP_COMMAND_STOR:
json_object_set_new(ftpd, "command", json_string("STOR"));
jb_set_string(jb, "command", "STOR");
break;
case FTP_COMMAND_RETR:
json_object_set_new(ftpd, "command", json_string("RETR"));
jb_set_string(jb, "command", "RETR");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasonish I still think we can optimize these constant cases with some preproc magic to become a single string that we append

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, need to look at this again.

break;
default:
break;
}
return ftpd;

jb_close(jb);
}

/**
* \brief Free memory allocated for global SMTP parser state.
* \brief Free memory allocated for global FTP parser state.
*/
void FTPParserCleanup(void)
{
Expand Down
6 changes: 4 additions & 2 deletions src/app-layer-ftp.h
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2010 Open Information Security Foundation
/* Copyright (C) 2007-2020 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
Expand All @@ -25,6 +25,8 @@
#ifndef __APP_LAYER_FTP_H__
#define __APP_LAYER_FTP_H__

#include "rust.h"

enum {
FTP_STATE_IN_PROGRESS,
FTP_STATE_PORT_DONE,
Expand Down Expand Up @@ -221,7 +223,7 @@ uint64_t FTPMemuseGlobalCounter(void);
uint64_t FTPMemcapGlobalCounter(void);

uint16_t JsonGetNextLineFromBuffer(const char *buffer, const uint16_t len);
json_t *JsonFTPDataAddMetadata(const Flow *f);
void JsonFTPDataAddMetadata(const Flow *f, JsonBuilder *jb);

#endif /* __APP_LAYER_FTP_H__ */

6 changes: 1 addition & 5 deletions src/output-json-alert.c
Expand Up @@ -540,11 +540,7 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
break;
}
case ALPROTO_FTPDATA:
hjs = JsonFTPDataAddMetadata(p->flow);
if (hjs) {
jb_set_jsont(jb, "ftp-data", hjs);
json_decref(hjs);
}
JsonFTPDataAddMetadata(p->flow, jb);
break;
case ALPROTO_DNP3:
AlertJsonDnp3(p->flow, pa->tx_id, jb);
Expand Down
108 changes: 63 additions & 45 deletions src/output-json-ftp.c
Expand Up @@ -60,41 +60,35 @@ typedef struct LogFTPLogThread_ {
MemBuffer *buffer;
} LogFTPLogThread;

static json_t *JsonFTPLogCommand(Flow *f, FTPTransaction *tx)
static void JsonFTPLogCommand(Flow *f, FTPTransaction *tx, JsonBuilder *jb)
{
json_t *cjs = json_object();
if (!cjs) {
return cjs;
}

/* Preallocate array objects to simplify failure case */
json_t *js_resplist = NULL;
json_t *js_respcode_list = NULL;
JsonBuilder *js_resplist = NULL;
JsonBuilder *js_respcode_list = NULL;
if (!TAILQ_EMPTY(&tx->response_list)) {
js_resplist = json_array();
js_respcode_list = json_array();
js_resplist = jb_new_array();
js_respcode_list = jb_new_array();

if (unlikely(js_resplist == NULL || js_respcode_list == NULL)) {
if (js_resplist) {
json_decref(js_resplist);
} else {
json_decref(js_respcode_list);
}
return cjs;
goto fail;
}
}

json_object_set_new(cjs, "command", json_string(tx->command_descriptor->command_name));
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) {
json_object_set_new(cjs, "command_data",
JsonAddStringN((const char *)tx->request + min_length,
tx->request_length - min_length));
char *s = BytesToString(tx->request + min_length,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasonish iirc we talked about a direct jb_set_bytes or something to avoid an expensive operation like this?

Copy link
Contributor Author

@jlucovsky jlucovsky Jun 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see jb_set_string_from_bytes ... That requires a null-terminated string which I don't have.

An interface that accepted a count and worked with a non-null-terminated string would be needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jb_set_string_from_bytes should work, just looks like it doesn't have an extern "C" wrapper yet (haven't needed it from C yet would be the reason).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See this PR: #5040

Should be able to:

jb_append_string_from_bytes(js_respcode_list, where, 3);

tx->request_length - min_length - 1);
if (s) {
jb_set_string(jb, "command_data", s);
SCFree(s);
}
} else {
json_object_set_new(cjs, "command_data", json_string(NULL));
jb_set_string(jb, "command_data", NULL);
}

if (!TAILQ_EMPTY(&tx->response_list)) {
int resp_code_cnt = 0;
int resp_cnt = 0;
FTPString *response;
TAILQ_FOREACH(response, &tx->response_list, next) {
/* handle multiple lines within the response, \r\n delimited */
Expand All @@ -107,40 +101,61 @@ static json_t *JsonFTPLogCommand(Flow *f, FTPTransaction *tx)
if (pos >= 3) {
/* Gather the completion code if present */
if (isdigit(where[0]) && isdigit(where[1]) && isdigit(where[2])) {
json_array_append_new(js_respcode_list,
JsonAddStringN((const char *)where, 3));
char *s = BytesToString(where, 3);
if (s != NULL) {
jb_append_string(js_respcode_list, s);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the goal of the jsonbuilder transition is to avoid creating temporary objects like this, but instead "stream" the whole object into a single jb.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modeled my implementation after that in output-json-email-common.c.

Note that I'm building 2 arrays as the response is processed and 2 arrays will be created during that. I'm not sure how the new interfaces support this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't in some cases. In DNS, to avoid looping throught the responses 2x, I had to use intermediate objects as well in dns_log_json_answer.

SCFree(s);
resp_code_cnt++;
}
offset = 4;
}
}
/* move past 3 character completion code */
if (pos >= offset) {
json_array_append_new(js_resplist,
JsonAddStringN((const char *)where + offset, pos - offset));
char *s = BytesToString(where + offset, pos - offset);
if (s != NULL) {
jb_append_string(js_resplist, s);
SCFree(s);
resp_cnt++;
}
}

where += pos;
length -= pos;
}
}

json_object_set_new(cjs, "reply", js_resplist);
json_object_set_new(cjs, "completion_code", js_respcode_list);
if (resp_cnt) {
jb_close(js_resplist);
jb_set_object(jb, "reply", js_resplist);
}
jb_free(js_resplist);
if (resp_code_cnt) {
jb_close(js_respcode_list);
jb_set_object(jb, "completion_code", js_respcode_list);
}
jb_free(js_respcode_list);
}

if (tx->dyn_port) {
json_object_set_new(cjs, "dynamic_port", json_integer(tx->dyn_port));
jb_set_uint(jb, "dynamic_port", tx->dyn_port);
}

if (tx->command_descriptor->command == FTP_COMMAND_PORT ||
tx->command_descriptor->command == FTP_COMMAND_EPRT) {
json_object_set_new(cjs, "mode",
json_string((char *)(tx->active ? "active" : "passive")));
jb_set_string(jb, "mode", tx->active ? "active" : "passive");
}

json_object_set_new(cjs, "reply_received",
json_string((char *)(tx->done ? "yes" : "no")));
jb_set_string(jb, "reply_received", tx->done ? "yes" : "no");

return;

return cjs;
fail:
if (js_resplist) {
jb_free(js_resplist);
} else {
jb_free(js_respcode_list);
}
}


Expand All @@ -159,27 +174,30 @@ static int JsonFTPLogger(ThreadVars *tv, void *thread_data,
LogFTPLogThread *thread = thread_data;
LogFTPFileCtx *ftp_ctx = thread->ftplog_ctx;

json_t *js = CreateJSONHeaderWithTxId(p, LOG_DIR_FLOW, event_type, tx_id);
if (likely(js)) {
JsonAddCommonOptions(&ftp_ctx->cfg, p, f, js);
json_t *cjs = NULL;
JsonBuilder *jb = CreateEveHeaderWithTxId(p, LOG_DIR_FLOW, event_type, NULL, tx_id);
if (likely(jb)) {
EveAddCommonOptions(&ftp_ctx->cfg, p, f, jb);
jb_open_object(jb, event_type);
if (f->alproto == ALPROTO_FTPDATA) {
cjs = JsonFTPDataAddMetadata(f);
JsonFTPDataAddMetadata(f, jb);
} else {
cjs = JsonFTPLogCommand(f, tx);
JsonFTPLogCommand(f, tx, jb);
}

if (cjs) {
json_object_set_new(js, event_type, cjs);
if (!jb_close(jb)) {
goto fail;
}

MemBufferReset(thread->buffer);
OutputJSONBuffer(js, thread->ftplog_ctx->file_ctx, &thread->buffer);
OutputJsonBuilderBuffer(jb, thread->ftplog_ctx->file_ctx, &thread->buffer);

json_object_clear(js);
json_decref(js);
jb_free(jb);
}
return TM_ECODE_OK;

fail:
jb_free(jb);
return TM_ECODE_FAILED;
}

static void OutputFTPLogDeInitCtxSub(OutputCtx *output_ctx)
Expand Down
19 changes: 1 addition & 18 deletions src/output-json.c
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2018 Open Information Security Foundation
/* Copyright (C) 2007-2020 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
Expand Down Expand Up @@ -135,23 +135,6 @@ json_t *SCJsonString(const char *val)
/* Default Sensor ID value */
static int64_t sensor_id = -1; /* -1 = not defined */

/**
* \brief Create a JSON string from a character sequence
*
* \param Pointer to character sequence
* \param Number of characters to use from the sequence
* \retval JSON object for the character sequence
*/
json_t *JsonAddStringN(const char *string, size_t size)
{
char tmpbuf[size + 1];

memcpy(tmpbuf, string, size);
tmpbuf[size] = '\0';

return SCJsonString(tmpbuf);
}

static void JsonAddPacketvars(const Packet *p, json_t *js_vars)
{
if (p == NULL || p->pktvar == NULL) {
Expand Down
3 changes: 1 addition & 2 deletions src/output-json.h
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2013 Open Information Security Foundation
/* Copyright (C) 2007-2020 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
Expand Down Expand Up @@ -118,7 +118,6 @@ typedef struct OutputJsonThreadCtx_ {

json_t *SCJsonBool(int val);
json_t *SCJsonString(const char *val);
json_t *JsonAddStringN(const char *string, size_t size);
void SCJsonDecref(json_t *js);

void JsonAddCommonOptions(const OutputJsonCommonSettings *cfg,
Expand Down