1 /* Copyright (C) 2014 Open Information Security Foundation
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
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.
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
21 * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
22 * \author Duarte Silva <duarte.silva@serializing.me>
25 #include "suricata-common.h"
28 #include "app-layer-parser.h"
29 #include "app-layer-htp.h"
30 #include "app-layer-htp-xff.h"
32 #include "util-misc.h"
33 #include "util-memrchr.h"
34 #include "util-unittest.h"
36 /** XFF header value minimal length */
37 #define XFF_CHAIN_MINLEN 7
38 /** XFF header value maximum length */
39 #define XFF_CHAIN_MAXLEN 256
40 /** Default XFF header name */
41 #define XFF_DEFAULT "X-Forwarded-For"
44 * \brief parse XFF string
45 * \param input input string, might be modified
46 * \param output output buffer
47 * \param output_size size of output buffer
48 * \retval bool 1 ok, 0 fail
50 static int ParseXFFString(char *input
, char *output
, int output_size
)
52 size_t len
= strlen(input
);
56 if (input
[0] == '[') {
57 char *end
= strchr(input
, ']');
58 if (end
== NULL
) // malformed, not closed
61 if (end
!= input
+(len
- 1)) {
62 SCLogDebug("data after closing bracket");
63 // if we ever want to parse the port, we can do it here
66 /* done, lets wrap up */
67 input
++; // skip past [
68 *end
= '\0'; // overwrite ], ignore anything after
71 /* lets see if the xff string ends in a port */
82 /* 3 dots: ipv4, one ':' port */
83 if (d
== 3 && c
== 1) {
84 SCLogDebug("XFF w port %s", input
);
85 char *x
= strchr(input
, ':');
88 SCLogDebug("XFF w/o port %s", input
);
89 // if we ever want to parse the port, we can do it here
94 SCLogDebug("XFF %s", input
);
96 /** Sanity check on extracted IP for IPv4 and IPv6 */
98 if (inet_pton(AF_INET
, input
, ip
) == 1 ||
99 inet_pton(AF_INET6
, input
, ip
) == 1)
101 strlcpy(output
, input
, output_size
);
108 * \brief Function to return XFF IP if any in the selected transaction. The
109 * caller needs to lock the flow.
110 * \retval 1 if the IP has been found and returned in dstbuf
111 * \retval 0 if the IP has not being found or error
113 int HttpXFFGetIPFromTx(const Flow
*f
, uint64_t tx_id
, HttpXFFCfg
*xff_cfg
,
114 char *dstbuf
, int dstbuflen
)
116 uint8_t xff_chain
[XFF_CHAIN_MAXLEN
];
117 HtpState
*htp_state
= NULL
;
119 uint64_t total_txs
= 0;
120 uint8_t *p_xff
= NULL
;
122 htp_state
= (HtpState
*)FlowGetAppState(f
);
124 if (htp_state
== NULL
) {
125 SCLogDebug("no http state, XFF IP cannot be retrieved");
129 total_txs
= AppLayerParserGetTxCnt(f
, htp_state
);
130 if (tx_id
>= total_txs
)
133 tx
= AppLayerParserGetTx(f
->proto
, ALPROTO_HTTP1
, htp_state
, tx_id
);
135 SCLogDebug("tx is NULL, XFF cannot be retrieved");
139 htp_header_t
*h_xff
= NULL
;
140 if (tx
->request_headers
!= NULL
) {
141 h_xff
= htp_table_get_c(tx
->request_headers
, xff_cfg
->header
);
144 if (h_xff
!= NULL
&& bstr_len(h_xff
->value
) >= XFF_CHAIN_MINLEN
&&
145 bstr_len(h_xff
->value
) < XFF_CHAIN_MAXLEN
) {
147 memcpy(xff_chain
, bstr_ptr(h_xff
->value
), bstr_len(h_xff
->value
));
148 xff_chain
[bstr_len(h_xff
->value
)]=0;
150 if (xff_cfg
->flags
& XFF_REVERSE
) {
151 /** Get the last IP address from the chain */
152 p_xff
= memrchr(xff_chain
, ' ', bstr_len(h_xff
->value
));
160 /** Get the first IP address from the chain */
161 p_xff
= memchr(xff_chain
, ',', bstr_len(h_xff
->value
));
167 return ParseXFFString((char *)p_xff
, dstbuf
, dstbuflen
);
173 * \brief Function to return XFF IP if any. The caller needs to lock the flow.
174 * \retval 1 if the IP has been found and returned in dstbuf
175 * \retval 0 if the IP has not being found or error
177 int HttpXFFGetIP(const Flow
*f
, HttpXFFCfg
*xff_cfg
, char *dstbuf
, int dstbuflen
)
179 HtpState
*htp_state
= NULL
;
181 uint64_t total_txs
= 0;
183 htp_state
= (HtpState
*)FlowGetAppState(f
);
184 if (htp_state
== NULL
) {
185 SCLogDebug("no http state, XFF IP cannot be retrieved");
189 total_txs
= AppLayerParserGetTxCnt(f
, htp_state
);
190 for (; tx_id
< total_txs
; tx_id
++) {
191 if (HttpXFFGetIPFromTx(f
, tx_id
, xff_cfg
, dstbuf
, dstbuflen
) == 1)
196 return 0; // Not found
200 * \brief Function to return XFF configuration from a configuration node.
202 void HttpXFFGetCfg(ConfNode
*conf
, HttpXFFCfg
*result
)
204 BUG_ON(result
== NULL
);
206 ConfNode
*xff_node
= NULL
;
209 xff_node
= ConfNodeLookupChild(conf
, "xff");
211 if (xff_node
!= NULL
&& ConfNodeChildValueIsTrue(xff_node
, "enabled")) {
212 const char *xff_mode
= ConfNodeLookupChildValue(xff_node
, "mode");
214 if (xff_mode
!= NULL
&& strcasecmp(xff_mode
, "overwrite") == 0) {
215 result
->flags
|= XFF_OVERWRITE
;
217 if (xff_mode
== NULL
) {
218 SCLogWarning(SC_WARN_XFF_INVALID_MODE
, "The XFF mode hasn't been defined, falling back to extra-data mode");
220 else if (strcasecmp(xff_mode
, "extra-data") != 0) {
221 SCLogWarning(SC_WARN_XFF_INVALID_MODE
, "The XFF mode %s is invalid, falling back to extra-data mode",
224 result
->flags
|= XFF_EXTRADATA
;
227 const char *xff_deployment
= ConfNodeLookupChildValue(xff_node
, "deployment");
229 if (xff_deployment
!= NULL
&& strcasecmp(xff_deployment
, "forward") == 0) {
230 result
->flags
|= XFF_FORWARD
;
232 if (xff_deployment
== NULL
) {
233 SCLogWarning(SC_WARN_XFF_INVALID_DEPLOYMENT
, "The XFF deployment hasn't been defined, falling back to reverse proxy deployment");
235 else if (strcasecmp(xff_deployment
, "reverse") != 0) {
236 SCLogWarning(SC_WARN_XFF_INVALID_DEPLOYMENT
, "The XFF mode %s is invalid, falling back to reverse proxy deployment",
239 result
->flags
|= XFF_REVERSE
;
242 const char *xff_header
= ConfNodeLookupChildValue(xff_node
, "header");
244 if (xff_header
!= NULL
) {
245 result
->header
= (char *) xff_header
;
247 SCLogWarning(SC_WARN_XFF_INVALID_HEADER
, "The XFF header hasn't been defined, using the default %s",
249 result
->header
= XFF_DEFAULT
;
253 result
->flags
= XFF_DISABLED
;
259 static int XFFTest01(void) {
260 char input
[] = "1.2.3.4:5678";
262 int r
= ParseXFFString(input
, output
, sizeof(output
));
263 FAIL_IF_NOT(r
== 1 && strcmp(output
, "1.2.3.4") == 0);
267 static int XFFTest02(void) {
268 char input
[] = "[12::34]:1234"; // thanks chort!
270 int r
= ParseXFFString(input
, output
, sizeof(output
));
271 FAIL_IF_NOT(r
== 1 && strcmp(output
, "12::34") == 0);
275 static int XFFTest03(void) {
276 char input
[] = "[2a03:2880:1010:3f02:face:b00c:0:2]:80"; // thanks chort!
278 int r
= ParseXFFString(input
, output
, sizeof(output
));
279 FAIL_IF_NOT(r
== 1 && strcmp(output
, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
283 static int XFFTest04(void) {
284 char input
[] = "[2a03:2880:1010:3f02:face:b00c:0:2]"; // thanks chort!
286 int r
= ParseXFFString(input
, output
, sizeof(output
));
287 FAIL_IF_NOT(r
== 1 && strcmp(output
, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
291 static int XFFTest05(void) {
292 char input
[] = "[::ffff:1.2.3.4]:1234"; // thanks double-p
294 int r
= ParseXFFString(input
, output
, sizeof(output
));
295 FAIL_IF_NOT(r
== 1 && strcmp(output
, "::ffff:1.2.3.4") == 0);
299 static int XFFTest06(void) {
300 char input
[] = "12::34";
302 int r
= ParseXFFString(input
, output
, sizeof(output
));
303 FAIL_IF_NOT(r
== 1 && strcmp(output
, "12::34") == 0);
307 static int XFFTest07(void) {
308 char input
[] = "1.2.3.4";
310 int r
= ParseXFFString(input
, output
, sizeof(output
));
311 FAIL_IF_NOT(r
== 1 && strcmp(output
, "1.2.3.4") == 0);
315 static int XFFTest08(void) {
316 char input
[] = "[1.2.3.4:1234";
318 int r
= ParseXFFString(input
, output
, sizeof(output
));
323 static int XFFTest09(void) {
324 char input
[] = "999.999.999.999:1234";
326 int r
= ParseXFFString(input
, output
, sizeof(output
));
333 void HTPXFFParserRegisterTests(void)
336 UtRegisterTest("XFFTest01", XFFTest01
);
337 UtRegisterTest("XFFTest02", XFFTest02
);
338 UtRegisterTest("XFFTest03", XFFTest03
);
339 UtRegisterTest("XFFTest04", XFFTest04
);
340 UtRegisterTest("XFFTest05", XFFTest05
);
341 UtRegisterTest("XFFTest06", XFFTest06
);
342 UtRegisterTest("XFFTest07", XFFTest07
);
343 UtRegisterTest("XFFTest08", XFFTest08
);
344 UtRegisterTest("XFFTest09", XFFTest09
);