Coverage Report

Created: 2026-11-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/output-json-ftp.c
Line
Count
Source
1
/* Copyright (C) 2017-2021 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Jeff Lucovsky <jeff@lucovsky.org>
22
 *
23
 * Implement JSON/eve logging app-layer FTP.
24
 */
25
26
#include "suricata-common.h"
27
#include "detect.h"
28
#include "pkt-var.h"
29
#include "conf.h"
30
31
#include "threads.h"
32
#include "threadvars.h"
33
#include "tm-threads.h"
34
35
#include "util-unittest.h"
36
#include "util-buffer.h"
37
#include "util-debug.h"
38
#include "util-mem.h"
39
40
#include "output.h"
41
#include "output-json.h"
42
43
#include "app-layer.h"
44
#include "app-layer-parser.h"
45
46
#include "app-layer-ftp.h"
47
#include "output-json-ftp.h"
48
49
bool EveFTPLogCommand(const void *vtx, void *jb)
50
94.1k
{
51
94.1k
    const FTPTransaction *tx = vtx;
52
    /* Preallocate array objects to simplify failure case */
53
94.1k
    SCJsonBuilder *js_resplist = NULL;
54
94.1k
    if (!TAILQ_EMPTY(&tx->response_list)) {
55
77.6k
        js_resplist = SCJbNewArray();
56
57
77.6k
        if (unlikely(js_resplist == NULL)) {
58
0
            return false;
59
0
        }
60
77.6k
    }
61
94.1k
    const char *command_name = NULL;
62
94.1k
    uint8_t command_name_length;
63
94.1k
    if (tx->command_descriptor.command_code != FTP_COMMAND_UNKNOWN) {
64
57.6k
        if (!SCGetFtpCommandInfo(tx->command_descriptor.command_index, &command_name, NULL,
65
57.6k
                    &command_name_length)) {
66
0
            SCLogDebug("Unable to fetch info for FTP command code %d [index %d]",
67
0
                    tx->command_descriptor.command_code, tx->command_descriptor.command_index);
68
0
            return false;
69
0
        }
70
57.6k
    }
71
94.1k
    SCJbOpenObject(jb, "ftp");
72
94.1k
    if (command_name) {
73
57.6k
        SCJbSetString(jb, "command", command_name);
74
57.6k
        uint32_t min_length = command_name_length + 1; /* command + space */
75
57.6k
        if (tx->request_length > min_length) {
76
45.3k
            SCJbSetStringFromBytes(jb, "command_data", (const uint8_t *)tx->request + min_length,
77
45.3k
                    tx->request_length - min_length - 1);
78
45.3k
            if (tx->request_truncated) {
79
353
                JB_SET_TRUE(jb, "command_truncated");
80
44.9k
            } else {
81
44.9k
                JB_SET_FALSE(jb, "command_truncated");
82
44.9k
            }
83
45.3k
        }
84
57.6k
    }
85
86
94.1k
    bool reply_truncated = false;
87
88
94.1k
    if (!TAILQ_EMPTY(&tx->response_list)) {
89
77.6k
        int resp_cnt = 0;
90
77.6k
        FTPResponseWrapper *wrapper;
91
77.6k
        bool is_cc_array_open = false;
92
394k
        TAILQ_FOREACH (wrapper, &tx->response_list, next) {
93
            /* handle multiple lines within the response, \r\n delimited */
94
394k
            if (!wrapper->response) {
95
0
                continue;
96
0
            }
97
394k
            FTPResponseLine *response = wrapper->response;
98
99
394k
            if (!reply_truncated && response->truncated) {
100
591
                reply_truncated = true;
101
591
            }
102
394k
            if (response->code_length > 0) {
103
45.2k
                if (!is_cc_array_open) {
104
38.1k
                    SCJbOpenArray(jb, "completion_code");
105
38.1k
                    is_cc_array_open = true;
106
38.1k
                }
107
45.2k
                SCJbAppendStringFromBytes(
108
45.2k
                        jb, (const uint8_t *)response->code, (uint32_t)response->code_length);
109
45.2k
            }
110
394k
            if (response->length) {
111
393k
                SCJbAppendStringFromBytes(js_resplist, (const uint8_t *)response->response,
112
393k
                        (uint32_t)response->length);
113
393k
                resp_cnt++;
114
393k
            }
115
394k
        }
116
117
77.6k
        if (is_cc_array_open) {
118
38.1k
            SCJbClose(jb);
119
38.1k
        }
120
77.6k
        if (resp_cnt) {
121
77.5k
            SCJbClose(js_resplist);
122
77.5k
            SCJbSetObject(jb, "reply", js_resplist);
123
77.5k
        }
124
77.6k
        SCJbFree(js_resplist);
125
77.6k
    }
126
127
94.1k
    if (tx->dyn_port) {
128
11.5k
        SCJbSetUint(jb, "dynamic_port", tx->dyn_port);
129
11.5k
    }
130
131
94.1k
    switch (tx->command_descriptor.command_code) {
132
10.0k
        case FTP_COMMAND_PORT:
133
12.7k
        case FTP_COMMAND_EPRT:
134
17.6k
        case FTP_COMMAND_PASV:
135
22.9k
        case FTP_COMMAND_EPSV:
136
22.9k
            if (tx->active) {
137
4.53k
                JB_SET_STRING(jb, "mode", "active");
138
18.3k
            } else {
139
18.3k
                JB_SET_STRING(jb, "mode", "passive");
140
18.3k
            }
141
94.1k
        default:
142
94.1k
            break;
143
94.1k
    }
144
145
94.1k
    if (tx->done) {
146
90.8k
        JB_SET_STRING(jb, "reply_received", "yes");
147
90.8k
    } else {
148
3.27k
        JB_SET_STRING(jb, "reply_received", "no");
149
3.27k
    }
150
151
94.1k
    if (reply_truncated) {
152
591
        JB_SET_TRUE(jb, "reply_truncated");
153
93.5k
    } else {
154
93.5k
        JB_SET_FALSE(jb, "reply_truncated");
155
93.5k
    }
156
94.1k
    SCJbClose(jb);
157
    return true;
158
94.1k
}