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 
32 static const char htp_tx[] = "suricata:http:tx";
33 
34 struct LuaTx {
35  htp_tx_t *tx;
36 };
37 
38 static 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 
61 static 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 
73 static 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 
81  return LuaPushStringBuffer(
82  luastate, bstr_ptr(htp_tx_request_uri(tx->tx)), bstr_len(htp_tx_request_uri(tx->tx)));
83 }
84 
85 static 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 
98  return LuaPushStringBuffer(
99  luastate, bstr_ptr(request_uri_normalized), bstr_len(request_uri_normalized));
100 }
101 
102 static 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 
114 static 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 
126 static 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 
154 static int LuaHttpGetRequestHeader(lua_State *luastate)
155 {
156  return LuaHttpGetHeader(luastate, 0 /* request */);
157 }
158 
159 static int LuaHttpGetResponseHeader(lua_State *luastate)
160 {
161  return LuaHttpGetHeader(luastate, 1 /* response */);
162 }
163 
164 static 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  if (htud == NULL)
173  return LuaCallbackError(luastate, "no htud in tx");
174 
175  uint8_t *raw = htud->request_headers_raw;
176  uint32_t raw_len = htud->request_headers_raw_len;
177  if (dir == 1) {
178  raw = htud->response_headers_raw;
179  raw_len = htud->response_headers_raw_len;
180  }
181 
182  if (raw == NULL || raw_len == 0)
183  return LuaCallbackError(luastate, "no raw headers");
184 
185  return LuaPushStringBuffer(luastate, raw, raw_len);
186 }
187 
188 static int LuaHttpGetRawRequestHeaders(lua_State *luastate)
189 {
190  return LuaHttpGetRawHeaders(luastate, 0);
191 }
192 
193 static int LuaHttpGetRawResponseHeaders(lua_State *luastate)
194 {
195  return LuaHttpGetRawHeaders(luastate, 1);
196 }
197 
198 static int LuaHttpGetHeaders(lua_State *luastate, int dir)
199 {
200  struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
201  if (tx == NULL) {
202  lua_pushnil(luastate);
203  return 1;
204  }
205 
206  const htp_headers_t *table = htp_tx_request_headers(tx->tx);
207  if (dir == 1)
208  table = htp_tx_response_headers(tx->tx);
209  if (table == NULL) {
210  lua_pushnil(luastate);
211  return 1;
212  }
213 
214  lua_newtable(luastate);
215  const htp_header_t *h = NULL;
216  size_t i = 0;
217  size_t no_of_headers = htp_headers_size(table);
218  for (; i < no_of_headers; i++) {
219  h = htp_headers_get_index(table, i);
220  LuaPushStringBuffer(luastate, htp_header_name_ptr(h), htp_header_name_len(h));
221  LuaPushStringBuffer(luastate, htp_header_value_ptr(h), htp_header_value_len(h));
222  lua_settable(luastate, -3);
223  }
224  return 1;
225 }
226 
227 /** \brief return request headers as lua table */
228 static int LuaHttpGetRequestHeaders(lua_State *luastate)
229 {
230  return LuaHttpGetHeaders(luastate, 0);
231 }
232 
233 /** \brief return response headers as lua table */
234 static int LuaHttpGetResponseHeaders(lua_State *luastate)
235 {
236  return LuaHttpGetHeaders(luastate, 1);
237 }
238 
239 static int LuaHttpGetBody(lua_State *luastate, int dir)
240 {
241  struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx);
242  if (tx == NULL) {
243  lua_pushnil(luastate);
244  return 1;
245  }
246 
247  HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx->tx);
248  if (htud == NULL)
249  return LuaCallbackError(luastate, "no htud in tx");
250 
251  HtpBody *body = NULL;
252  if (dir == 0)
253  body = &htud->request_body;
254  else
255  body = &htud->response_body;
256 
257  if (body->first == NULL) {
258  return LuaCallbackError(luastate, "no body found");
259  }
260 
261  int index = 1;
262  HtpBodyChunk *chunk = body->first;
263  lua_newtable(luastate);
264  while (chunk != NULL) {
265  lua_pushinteger(luastate, index);
266 
267  const uint8_t *data = NULL;
268  uint32_t data_len = 0;
269  StreamingBufferSegmentGetData(body->sb, &chunk->sbseg, &data, &data_len);
270  LuaPushStringBuffer(luastate, data, data_len);
271 
272  lua_settable(luastate, -3);
273 
274  chunk = chunk->next;
275  index++;
276  }
277 
278  if (body->first && body->last) {
279  lua_pushinteger(luastate, body->first->sbseg.stream_offset);
280  lua_pushinteger(luastate, body->last->sbseg.stream_offset + body->last->sbseg.segment_len);
281  return 3;
282  } else {
283  return 1;
284  }
285 }
286 
287 static int LuaHttpGetRequestBody(lua_State *luastate)
288 {
289  return LuaHttpGetBody(luastate, 0);
290 }
291 
292 static int LuaHttpGetResponseBody(lua_State *luastate)
293 {
294  return LuaHttpGetBody(luastate, 1);
295 }
296 
297 static const struct luaL_Reg txlib[] = {
298  // clang-format off
299  {"request_header", LuaHttpGetRequestHeader},
300  {"response_header", LuaHttpGetResponseHeader},
301  {"request_line", LuaHttpGetRequestLine},
302  {"response_line", LuaHttpGetResponseLine},
303  {"request_headers_raw", LuaHttpGetRawRequestHeaders},
304  {"response_headers_raw", LuaHttpGetRawResponseHeaders},
305  {"request_uri_raw", LuaHttpGetRequestUriRaw},
306  {"request_uri_normalized", LuaHttpGetRequestUriNormalized},
307  {"request_headers", LuaHttpGetRequestHeaders},
308  {"response_headers", LuaHttpGetResponseHeaders},
309  {"request_host", LuaHttpGetRequestHost},
310  {"request_body", LuaHttpGetRequestBody},
311  {"response_body", LuaHttpGetResponseBody},
312  {NULL, NULL,},
313  // clang-format on
314 };
315 
316 static const struct luaL_Reg htplib[] = {
317  // clang-format off
318  {"get_tx", LuaHttpGetTx },
319  {NULL, NULL,},
320  // clang-format on
321 };
322 
324 {
325  luaL_newmetatable(luastate, htp_tx);
326  lua_pushvalue(luastate, -1);
327  lua_setfield(luastate, -2, "__index");
328  luaL_setfuncs(luastate, txlib, 0);
329  luaL_newlib(luastate, htplib);
330  return 1;
331 }
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:59
HtpBody_::last
HtpBodyChunk * last
Definition: app-layer-htp.h:132
HtpTxUserData_::request_body
HtpBody request_body
Definition: app-layer-htp.h:164
LuaTx::tx
DNSTransaction * tx
Definition: util-lua-dns.c:35
LuaGetStringArgument
const char * LuaGetStringArgument(lua_State *luastate, int idx)
Definition: util-lua-common.c:66
HtpBody_::first
HtpBodyChunk * first
Definition: app-layer-htp.h:131
lua_State
struct lua_State lua_State
Definition: suricata-common.h:515
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:323
name
const char * name
Definition: tm-threads.c:2135
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
htp_tx_t * tx
Definition: util-lua-http.c:35
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:560
LuaPushStringBuffer
int LuaPushStringBuffer(lua_State *luastate, const uint8_t *input, size_t input_len)
Definition: util-lua.c:319