htp/xff: rustify htp_xff_get_ip

pull/16338/head
Philippe Antoine 4 months ago committed by Victor Julien
parent b6a52e471a
commit cd347f012a

@ -521,3 +521,25 @@ pub unsafe extern "C" fn htp_tx_data_len(data: *const Data) -> isize {
pub unsafe extern "C" fn htp_tx_data_is_empty(data: *const Data) -> bool {
data.as_ref().map(|data| data.is_empty()).unwrap_or(true)
}
/// Return the ip address from the Xff header
///
/// Returns 1 if there is an xff ip, 0 otherwise
/// # Safety
/// When calling this method, you have to ensure that the destination buffer is correctly allocated
/// and supply a non-NULL tx, and a valid CString for header
#[no_mangle]
pub unsafe extern "C" fn htp_xff_get_ip(
tx: *const Transaction, reversed: bool, header: *const libc::c_char, dst: *mut u8, dst_len: u32,
) -> libc::c_int {
let dst = std::slice::from_raw_parts_mut(dst, dst_len as usize);
let h_xff = htp_headers_get(&(*tx).request_headers, header);
if h_xff.is_null() {
return 0;
}
if xff_ip(reversed, &(*h_xff).value, dst) {
1
} else {
0
}
}

@ -14,10 +14,11 @@ use crate::{
util::{validate_hostname, FlagOperations, HtpFlags},
HtpStatus,
};
use std::any::Any;
#[cfg(test)]
use std::cmp::Ordering;
use std::net::IpAddr;
use std::str::FromStr;
#[derive(Debug, Clone)]
/// This structure is used to pass transaction data (for example
@ -962,6 +963,74 @@ impl Transaction {
}
}
/// Normalize a previously-parsed request URI.
pub(crate) fn xff_ip(reversed: bool, h_xff: &[u8], dst: &mut [u8]) -> bool {
if h_xff.len() < XFF_CHAIN_MINLEN || h_xff.len() >= XFF_CHAIN_MAXLEN {
return false;
}
let ipbuf = if reversed {
/* Get the last IP address from the chain */
let cut = h_xff
.iter()
.rev()
.position(|&c| c == b' ')
.unwrap_or(h_xff.len());
&h_xff[h_xff.len() - cut..]
} else {
/* Get the first IP address from the chain */
let cut = h_xff
.iter()
.position(|&c| c == b',')
.unwrap_or(h_xff.len());
&h_xff[..cut]
};
parse_xff_string(ipbuf, dst)
}
fn parse_xff_string(ipbuf: &[u8], dst: &mut [u8]) -> bool {
if ipbuf.is_empty() {
return false;
}
let input = if ipbuf[0] == b'[' {
let cut = match ipbuf.iter().position(|&c| c == b']') {
Some(c) => c,
_ => {
return false;
}
};
&ipbuf[1..cut]
} else {
let mut c = 0;
let mut d = 0;
let mut posc = 0;
for (i, p) in ipbuf.iter().enumerate() {
if *p == b':' {
posc = i;
c += 1;
} else if *p == b'.' {
d += 1;
}
}
if d == 3 && c == 1 {
&ipbuf[..posc]
} else {
ipbuf
}
};
if IpAddr::from_str(std::str::from_utf8(input).unwrap_or_default()).is_ok() {
if dst.len() < input.len() + 1 {
return false;
}
dst[..input.len()].copy_from_slice(input);
dst[input.len()] = 0;
return true;
}
false
}
const XFF_CHAIN_MINLEN: usize = 7;
const XFF_CHAIN_MAXLEN: usize = 256;
impl PartialEq for Transaction {
/// Determines if other references the same transaction.
fn eq(&self, other: &Self) -> bool {
@ -1005,3 +1074,48 @@ fn GetNocaseNozero() {
result = t.get_nocase("key1");
assert!(result.is_none());
}
#[test]
fn test_parse_xff_string() {
let mut dst = [0u8; 64];
assert!(parse_xff_string(b"1.2.3.4:5678", &mut dst));
assert_eq!(&dst[..8], b"1.2.3.4\0");
assert!(parse_xff_string(b"[12::34]:1234", &mut dst));
assert_eq!(&dst[..7], b"12::34\0");
assert!(parse_xff_string(
b"[2a03:2880:1010:3f02:face:b00c:0:2]:80",
&mut dst
));
assert_eq!(&dst[..34], b"2a03:2880:1010:3f02:face:b00c:0:2\0");
assert!(parse_xff_string(
b"[2a03:2880:1010:3f02:face:b00c:0:2]",
&mut dst
));
assert_eq!(&dst[..34], b"2a03:2880:1010:3f02:face:b00c:0:2\0");
assert!(parse_xff_string(b"[::ffff:1.2.3.4]:1234", &mut dst));
assert_eq!(&dst[..15], b"::ffff:1.2.3.4\0");
assert!(parse_xff_string(b"12::34", &mut dst));
assert_eq!(&dst[..7], b"12::34\0");
assert!(parse_xff_string(b"1.2.3.4", &mut dst));
assert_eq!(&dst[..8], b"1.2.3.4\0");
assert!(!parse_xff_string(b"[1.2.3.4:1234", &mut dst));
assert!(!parse_xff_string(b"999.999.999.999:1234", &mut dst));
}
#[test]
fn test_xff_ip() {
let mut dst = [0u8; 64];
assert!(xff_ip(true, b"1.2.3.4", &mut dst));
assert_eq!(&dst[..8], b"1.2.3.4\0");
let mut too_short = [0u8; 4];
assert!(!xff_ip(true, b"1.2.3.4", &mut too_short));
}

@ -36,110 +36,13 @@
#include "util-misc.h"
#include "util-unittest.h"
/** XFF header value minimal length */
#define XFF_CHAIN_MINLEN 7
/** XFF header value maximum length */
#define XFF_CHAIN_MAXLEN 256
/** Default XFF header name */
#define XFF_DEFAULT "X-Forwarded-For"
/** \internal
* \brief parse XFF string
* \param input input string, might be modified
* \param output output buffer
* \param output_size size of output buffer
* \retval bool 1 ok, 0 fail
*/
static int ParseXFFString(char *input, char *output, int output_size)
{
size_t len = strlen(input);
if (len == 0)
return 0;
if (input[0] == '[') {
char *end = strchr(input, ']');
if (end == NULL) // malformed, not closed
return 0;
if (end != input+(len - 1)) {
SCLogDebug("data after closing bracket");
// if we ever want to parse the port, we can do it here
}
/* done, lets wrap up */
input++; // skip past [
*end = '\0'; // overwrite ], ignore anything after
} else {
/* lets see if the xff string ends in a port */
int c = 0;
int d = 0;
char *p = input;
while (*p != '\0') {
if (*p == ':')
c++;
if (*p == '.')
d++;
p++;
}
/* 3 dots: ipv4, one ':' port */
if (d == 3 && c == 1) {
SCLogDebug("XFF w port %s", input);
char *x = strchr(input, ':');
if (x) {
*x = '\0';
SCLogDebug("XFF w/o port %s", input);
// if we ever want to parse the port, we can do it here
}
}
}
SCLogDebug("XFF %s", input);
/** Sanity check on extracted IP for IPv4 and IPv6 */
uint32_t ip[4];
if (inet_pton(AF_INET, input, ip) == 1 ||
inet_pton(AF_INET6, input, ip) == 1)
{
strlcpy(output, input, output_size);
return 1; // OK
}
return 0;
}
static int HttpXFFGetIPFromTxAux(htp_tx_t *tx, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen)
{
uint8_t xff_chain[XFF_CHAIN_MAXLEN];
uint8_t *p_xff = NULL;
const htp_header_t *h_xff = htp_tx_request_header(tx, xff_cfg->header);
if (h_xff != NULL && htp_header_value_len(h_xff) >= XFF_CHAIN_MINLEN &&
htp_header_value_len(h_xff) < XFF_CHAIN_MAXLEN) {
memcpy(xff_chain, htp_header_value_ptr(h_xff), htp_header_value_len(h_xff));
xff_chain[htp_header_value_len(h_xff)] = 0;
if (xff_cfg->flags & XFF_REVERSE) {
/** Get the last IP address from the chain */
p_xff = memrchr(xff_chain, ' ', htp_header_value_len(h_xff));
if (p_xff == NULL) {
p_xff = xff_chain;
} else {
p_xff++;
}
}
else {
/** Get the first IP address from the chain */
p_xff = memchr(xff_chain, ',', htp_header_value_len(h_xff));
if (p_xff != NULL) {
*p_xff = 0;
}
p_xff = xff_chain;
}
return ParseXFFString((char *)p_xff, dstbuf, dstbuflen);
}
return 0;
return htp_xff_get_ip(
tx, xff_cfg->flags & XFF_REVERSE, xff_cfg->header, (uint8_t *)dstbuf, dstbuflen);
}
/**
@ -265,94 +168,3 @@ void HttpXFFGetCfg(SCConfNode *conf, HttpXFFCfg *result)
result->flags = XFF_DISABLED;
}
}
#ifdef UNITTESTS
static int XFFTest01(void) {
char input[] = "1.2.3.4:5678";
char output[16];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 1 && strcmp(output, "1.2.3.4") == 0);
PASS;
}
static int XFFTest02(void) {
char input[] = "[12::34]:1234"; // thanks chort!
char output[16];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 1 && strcmp(output, "12::34") == 0);
PASS;
}
static int XFFTest03(void) {
char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]:80"; // thanks chort!
char output[46];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
PASS;
}
static int XFFTest04(void) {
char input[] = "[2a03:2880:1010:3f02:face:b00c:0:2]"; // thanks chort!
char output[46];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 1 && strcmp(output, "2a03:2880:1010:3f02:face:b00c:0:2") == 0);
PASS;
}
static int XFFTest05(void) {
char input[] = "[::ffff:1.2.3.4]:1234"; // thanks double-p
char output[46];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 1 && strcmp(output, "::ffff:1.2.3.4") == 0);
PASS;
}
static int XFFTest06(void) {
char input[] = "12::34";
char output[46];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 1 && strcmp(output, "12::34") == 0);
PASS;
}
static int XFFTest07(void) {
char input[] = "1.2.3.4";
char output[46];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 1 && strcmp(output, "1.2.3.4") == 0);
PASS;
}
static int XFFTest08(void) {
char input[] = "[1.2.3.4:1234";
char output[46];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 0);
PASS;
}
static int XFFTest09(void) {
char input[] = "999.999.999.999:1234";
char output[46];
int r = ParseXFFString(input, output, sizeof(output));
FAIL_IF_NOT(r == 0);
PASS;
}
#endif
void HTPXFFParserRegisterTests(void)
{
#ifdef UNITTESTS
UtRegisterTest("XFFTest01", XFFTest01);
UtRegisterTest("XFFTest02", XFFTest02);
UtRegisterTest("XFFTest03", XFFTest03);
UtRegisterTest("XFFTest04", XFFTest04);
UtRegisterTest("XFFTest05", XFFTest05);
UtRegisterTest("XFFTest06", XFFTest06);
UtRegisterTest("XFFTest07", XFFTest07);
UtRegisterTest("XFFTest08", XFFTest08);
UtRegisterTest("XFFTest09", XFFTest09);
#endif
}

@ -49,6 +49,4 @@ int HttpXFFGetIPFromTx(const Flow *f, uint64_t tx_id, HttpXFFCfg *xff_cfg, char
int HttpXFFGetIP(const Flow *f, HttpXFFCfg *xff_cfg, char *dstbuf, int dstbuflen);
void HTPXFFParserRegisterTests(void);
#endif /* SURICATA_APP_LAYER_HTP_XFF_H */

@ -5733,7 +5733,6 @@ static void HTPParserRegisterTests(void)
UtRegisterTest("HTPParserTest27", HTPParserTest27);
HTPFileParserRegisterTests();
HTPXFFParserRegisterTests();
}
#endif /* UNITTESTS */

Loading…
Cancel
Save