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