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