suricata
util-lua-http.c
Go to the documentation of this file.
1/* Copyright (C) 2014-2025 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 Victor Julien <victor@inliniac.net>
22 *
23 */
24
25#include "suricata-common.h"
26
27#include "app-layer-htp.h"
28#include "util-lua.h"
29#include "util-lua-common.h"
30#include "util-lua-http.h"
31
32static const char htp_tx[] = "suricata:http:tx";
33
34struct LuaTx {
35 htp_tx_t *tx;
36};
37
38static int LuaHttpGetTx(lua_State *luastate)
39{
40 if (!LuaStateNeedProto(luastate, ALPROTO_HTTP1)) {
41 return LuaCallbackError(luastate, "error: protocol not http");
42 }
43
44 htp_tx_t *tx = LuaStateGetTX(luastate);
45 if (tx == NULL) {
46 return LuaCallbackError(luastate, "error: no tx available");
47 }
48 struct LuaTx *ltx = (struct LuaTx *)lua_newuserdata(luastate, sizeof(*ltx));
49 if (ltx == NULL) {
50 return LuaCallbackError(luastate, "error: failed to allocate user data");
51 }
52
53 ltx->tx = tx;
54
55 luaL_getmetatable(luastate, htp_tx);
56 lua_setmetatable(luastate, -2);
57
58 return 1;
59}
60
61static int LuaHttpGetRequestHost(lua_State *luastate)
62{
63 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
64 if (tx == NULL) {
65 lua_pushnil(luastate);
66 return 1;
67 }
68
69 return LuaPushStringBuffer(luastate, bstr_ptr(htp_tx_request_hostname(tx->tx)),
70 bstr_len(htp_tx_request_hostname(tx->tx)));
71}
72
73static int LuaHttpGetRequestUriRaw(lua_State *luastate)
74{
75 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
76 if (tx == NULL) {
77 lua_pushnil(luastate);
78 return 1;
79 }
80
82 luastate, bstr_ptr(htp_tx_request_uri(tx->tx)), bstr_len(htp_tx_request_uri(tx->tx)));
83}
84
85static int LuaHttpGetRequestUriNormalized(lua_State *luastate)
86{
87 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
88 if (tx == NULL) {
89 lua_pushnil(luastate);
90 return 1;
91 }
92 bstr *request_uri_normalized = (bstr *)htp_tx_normalized_uri(tx->tx);
93
94 if (request_uri_normalized == NULL || bstr_ptr(request_uri_normalized) == NULL ||
95 bstr_len(request_uri_normalized) == 0)
96 return LuaCallbackError(luastate, "no normalized uri");
97
99 luastate, bstr_ptr(request_uri_normalized), bstr_len(request_uri_normalized));
100}
101
102static int LuaHttpGetRequestLine(lua_State *luastate)
103{
104 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
105 if (tx == NULL) {
106 lua_pushnil(luastate);
107 return 1;
108 }
109
110 return LuaPushStringBuffer(
111 luastate, bstr_ptr(htp_tx_request_line(tx->tx)), bstr_len(htp_tx_request_line(tx->tx)));
112}
113
114static int LuaHttpGetResponseLine(lua_State *luastate)
115{
116 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
117 if (tx == NULL) {
118 lua_pushnil(luastate);
119 return 1;
120 }
121
122 return LuaPushStringBuffer(luastate, bstr_ptr(htp_tx_response_line(tx->tx)),
123 bstr_len(htp_tx_response_line(tx->tx)));
124}
125
126static int LuaHttpGetHeader(lua_State *luastate, int dir)
127{
128 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
129 if (tx == NULL) {
130 lua_pushnil(luastate);
131 return 1;
132 }
133
134 /* since arg was added at last, it must be on top of the stack */
135 const char *name = LuaGetStringArgument(luastate, lua_gettop(luastate));
136 if (name == NULL) {
137 return LuaCallbackError(luastate, "argument missing, empty or wrong type");
138 }
139
140 const htp_header_t *h = NULL;
141 if (dir == 0) {
142 h = htp_tx_request_header(tx->tx, name);
143 } else {
144 h = htp_tx_response_header(tx->tx, name);
145 }
146
147 if (h == NULL || htp_header_value_len(h) == 0) {
148 return LuaCallbackError(luastate, "header not found");
149 }
150
151 return LuaPushStringBuffer(luastate, htp_header_value_ptr(h), htp_header_value_len(h));
152}
153
154static int LuaHttpGetRequestHeader(lua_State *luastate)
155{
156 return LuaHttpGetHeader(luastate, 0 /* request */);
157}
158
159static int LuaHttpGetResponseHeader(lua_State *luastate)
160{
161 return LuaHttpGetHeader(luastate, 1 /* response */);
162}
163
164static int LuaHttpGetRawHeaders(lua_State *luastate, int dir)
165{
166 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
167 if (tx == NULL) {
168 lua_pushnil(luastate);
169 return 1;
170 }
171 HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx->tx);
172
173 uint8_t *raw = htud->request_headers_raw;
174 uint32_t raw_len = htud->request_headers_raw_len;
175 if (dir == 1) {
176 raw = htud->response_headers_raw;
177 raw_len = htud->response_headers_raw_len;
178 }
179
180 if (raw == NULL || raw_len == 0)
181 return LuaCallbackError(luastate, "no raw headers");
182
183 return LuaPushStringBuffer(luastate, raw, raw_len);
184}
185
186static int LuaHttpGetRawRequestHeaders(lua_State *luastate)
187{
188 return LuaHttpGetRawHeaders(luastate, 0);
189}
190
191static int LuaHttpGetRawResponseHeaders(lua_State *luastate)
192{
193 return LuaHttpGetRawHeaders(luastate, 1);
194}
195
196static int LuaHttpGetHeaders(lua_State *luastate, int dir)
197{
198 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
199 if (tx == NULL) {
200 lua_pushnil(luastate);
201 return 1;
202 }
203
204 const htp_headers_t *table = htp_tx_request_headers(tx->tx);
205 if (dir == 1)
206 table = htp_tx_response_headers(tx->tx);
207 if (table == NULL) {
208 lua_pushnil(luastate);
209 return 1;
210 }
211
212 lua_newtable(luastate);
213 const htp_header_t *h = NULL;
214 size_t i = 0;
215 size_t no_of_headers = htp_headers_size(table);
216 for (; i < no_of_headers; i++) {
217 h = htp_headers_get_index(table, i);
218 LuaPushStringBuffer(luastate, htp_header_name_ptr(h), htp_header_name_len(h));
219 LuaPushStringBuffer(luastate, htp_header_value_ptr(h), htp_header_value_len(h));
220 lua_settable(luastate, -3);
221 }
222 return 1;
223}
224
225/** \brief return request headers as lua table */
226static int LuaHttpGetRequestHeaders(lua_State *luastate)
227{
228 return LuaHttpGetHeaders(luastate, 0);
229}
230
231/** \brief return response headers as lua table */
232static int LuaHttpGetResponseHeaders(lua_State *luastate)
233{
234 return LuaHttpGetHeaders(luastate, 1);
235}
236
237static int LuaHttpGetBody(lua_State *luastate, int dir)
238{
239 struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
240 if (tx == NULL) {
241 lua_pushnil(luastate);
242 return 1;
243 }
244
245 HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx->tx);
246
247 HtpBody *body = NULL;
248 if (dir == 0)
249 body = &htud->request_body;
250 else
251 body = &htud->response_body;
252
253 if (body->first == NULL) {
254 return LuaCallbackError(luastate, "no body found");
255 }
256
257 int index = 1;
258 HtpBodyChunk *chunk = body->first;
259 lua_newtable(luastate);
260 while (chunk != NULL) {
261 lua_pushinteger(luastate, index);
262
263 const uint8_t *data = NULL;
264 uint32_t data_len = 0;
265 StreamingBufferSegmentGetData(body->sb, &chunk->sbseg, &data, &data_len);
266 LuaPushStringBuffer(luastate, data, data_len);
267
268 lua_settable(luastate, -3);
269
270 chunk = chunk->next;
271 index++;
272 }
273
274 if (body->first && body->last) {
275 lua_pushinteger(luastate, body->first->sbseg.stream_offset);
276 lua_pushinteger(luastate, body->last->sbseg.stream_offset + body->last->sbseg.segment_len);
277 return 3;
278 } else {
279 return 1;
280 }
281}
282
283static int LuaHttpGetRequestBody(lua_State *luastate)
284{
285 return LuaHttpGetBody(luastate, 0);
286}
287
288static int LuaHttpGetResponseBody(lua_State *luastate)
289{
290 return LuaHttpGetBody(luastate, 1);
291}
292
293static const struct luaL_Reg txlib[] = {
294 // clang-format off
295 {"request_header", LuaHttpGetRequestHeader},
296 {"response_header", LuaHttpGetResponseHeader},
297 {"request_line", LuaHttpGetRequestLine},
298 {"response_line", LuaHttpGetResponseLine},
299 {"request_headers_raw", LuaHttpGetRawRequestHeaders},
300 {"response_headers_raw", LuaHttpGetRawResponseHeaders},
301 {"request_uri_raw", LuaHttpGetRequestUriRaw},
302 {"request_uri_normalized", LuaHttpGetRequestUriNormalized},
303 {"request_headers", LuaHttpGetRequestHeaders},
304 {"response_headers", LuaHttpGetResponseHeaders},
305 {"request_host", LuaHttpGetRequestHost},
306 {"request_body", LuaHttpGetRequestBody},
307 {"response_body", LuaHttpGetResponseBody},
308 {NULL, NULL,},
309 // clang-format on
310};
311
312static const struct luaL_Reg htplib[] = {
313 // clang-format off
314 {"get_tx", LuaHttpGetTx },
315 {NULL, NULL,},
316 // clang-format on
317};
318
320{
321 luaL_newmetatable(luastate, htp_tx);
322 lua_pushvalue(luastate, -1);
323 lua_setfield(luastate, -2, "__index");
324 luaL_setfuncs(luastate, txlib, 0);
325 luaL_newlib(luastate, htplib);
326 return 1;
327}
@ ALPROTO_HTTP1
StreamingBufferSegment sbseg
struct HtpBodyChunk_ * next
HtpBodyChunk * last
HtpBodyChunk * first
StreamingBuffer * sb
uint32_t request_headers_raw_len
uint8_t * request_headers_raw
HtpBody response_body
uint8_t * response_headers_raw
uint32_t response_headers_raw_len
htp_tx_t * tx
DNSTransaction * tx
struct lua_State lua_State
const char * name
int LuaStateNeedProto(lua_State *luastate, AppProto alproto)
const char * LuaGetStringArgument(lua_State *luastate, int idx)
int LuaCallbackError(lua_State *luastate, const char *msg)
int SCLuaLoadHttpLib(lua_State *luastate)
int LuaPushStringBuffer(lua_State *luastate, const uint8_t *input, size_t input_len)
Definition util-lua.c:319
void * LuaStateGetTX(lua_State *luastate)
get tx pointer from the lua state
Definition util-lua.c:134
void StreamingBufferSegmentGetData(const StreamingBuffer *sb, const StreamingBufferSegment *seg, const uint8_t **data, uint32_t *data_len)