From 38745b9ea65aaa048a924ae459ad7381b8a3a4df Mon Sep 17 00:00:00 2001 From: Aaron Bedra Date: Wed, 8 Apr 2015 11:22:11 -0500 Subject: [PATCH] Refactor xff parsing. This gets rid of the pcre dependency and uses the proper code for validating ip addresses. This also enables support for IPv6 addresses. The NGINX module has this code in it already, but it can be used across both modules, so they will eventually just call this. --- src/repsheet.h | 2 +- src/xff.c | 58 ++++++++++++++++++++++++++++++------------------- src/xff.h | 2 +- test/xff_test.c | 28 +++++++++++++++++++----- 4 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/repsheet.h b/src/repsheet.h index 8995ace..4812401 100644 --- a/src/repsheet.h +++ b/src/repsheet.h @@ -27,7 +27,7 @@ int check_connection(redisContext *context); int country_status(redisContext *context, const char *country_code); int actor_status(redisContext *context, const char *actor, int type, char *reason); -const char *remote_address(char *connected_address, const char *xff_header); +int remote_address(char *connected_address, char *xff_header, char *address); int blacklist_actor(redisContext *context, const char *actor, int type, const char *reason); int mark_actor(redisContext *context, const char *actor, int type, const char *reason); diff --git a/src/xff.c b/src/xff.c index 2146b76..b107ca8 100644 --- a/src/xff.c +++ b/src/xff.c @@ -1,6 +1,8 @@ -#include +#include #include +#include +#include "repsheet.h" #include "xff.h" /** @@ -18,30 +20,42 @@ * @param connected_address The IP of the connection to the server * @param xff_header The contents of the X-Forwarded-For header */ -const char *remote_address(char *connected_address, const char *xff_header) +int remote_address(char *connected_address, char *xff_header, char *address) { - if (xff_header == NULL) { - return connected_address; + if ((connected_address == NULL && xff_header == NULL) || address == NULL) { + return NIL; } - int error_offset, subvector[30]; - const char *error, *address; - - pcre *re_compiled; - - char *regex = "\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b"; - - re_compiled = pcre_compile(regex, 0, &error, &error_offset, NULL); - - int matches = pcre_exec(re_compiled, 0, xff_header, strlen(xff_header), 0, 0, subvector, 30); - - if(matches < 0) { - return NULL; + int length; + memset(address, '\0', INET6_ADDRSTRLEN); + + if (xff_header != NULL) { + char *p; + + for (p = xff_header; p < (xff_header + strlen(xff_header)); p++) { + if (*p == ' ' || *p == ',') { + break; + } + } + + length = p - xff_header; + char *test_address = malloc(length + 1); + memcpy(test_address, xff_header, length); + test_address[length] = '\0'; + + unsigned char buf[sizeof(struct in_addr)]; + unsigned char buf6[sizeof(struct in6_addr)]; + + if (inet_pton(AF_INET, (const char *)test_address, buf) == 1 || inet_pton(AF_INET6, (const char *)test_address, buf6) == 1) { + memcpy(address, test_address, length); + free(test_address); + return LIBREPSHEET_OK; + } else { + free(test_address); + return BLACKLISTED; + } } else { - pcre_get_substring(xff_header, subvector, matches, 0, &(address)); + memcpy(address, connected_address, strlen(connected_address)); + return LIBREPSHEET_OK; } - - pcre_free(re_compiled); - - return address; } diff --git a/src/xff.h b/src/xff.h index 394b5f7..9424869 100644 --- a/src/xff.h +++ b/src/xff.h @@ -1,6 +1,6 @@ #ifndef __XFF_H #define __XFF_H -const char *remote_address(char *connected_address, const char *xff_header); +int remote_address(char *connected_address, char *xff_header, char *address); #endif diff --git a/test/xff_test.c b/test/xff_test.c index 309a00c..4ab9359 100644 --- a/test/xff_test.c +++ b/test/xff_test.c @@ -1,28 +1,44 @@ +#include #include "../src/xff.h" +#include "../src/repsheet.h" #include "check.h" START_TEST(returns_null_when_headers_are_null) { - fail_unless(remote_address(NULL, NULL) == NULL); + fail_unless(remote_address(NULL, NULL, NULL) == NIL); } END_TEST START_TEST(processes_a_single_address) { - ck_assert_str_eq(remote_address("192.168.1.100", NULL), "192.168.1.100"); + char address[INET6_ADDRSTRLEN]; + int result = remote_address("192.168.1.100", NULL, address); + ck_assert_int_eq(result, LIBREPSHEET_OK); + ck_assert_str_eq(address, "192.168.1.100"); } END_TEST START_TEST(extract_only_the_first_ip_address) { - ck_assert_str_eq(remote_address("1.1.1.1", "8.8.8.8 12.34.56.78, 212.23.230.15"), "8.8.8.8"); + char address[INET6_ADDRSTRLEN]; + int result = remote_address("1.1.1.1", "8.8.8.8 12.34.56.78, 212.23.230.15", address); + ck_assert_int_eq(result, LIBREPSHEET_OK); + ck_assert_str_eq(address, "8.8.8.8"); } END_TEST START_TEST(ignores_user_generated_noise) { - ck_assert_str_eq(remote_address("1.1.1.1", "\\x5000 8.8.8.8, 12.23.45.67"), "8.8.8.8"); - ck_assert_str_eq(remote_address("1.1.1.1", "This is not an IP address 8.8.8.8, 12.23.45.67"), "8.8.8.8"); - ck_assert_str_eq(remote_address("1.1.1.1", "999.999.999.999, 8.8.8.8, 12.23.45.67"), "8.8.8.8"); + char address[INET6_ADDRSTRLEN]; + int result; + + result = remote_address("1.1.1.1", "\\x5000 8.8.8.8, 12.23.45.67", address); + ck_assert_int_eq(result, BLACKLISTED); + + result = remote_address("1.1.1.1", "This is not an IP address 8.8.8.8, 12.23.45.67", address); + ck_assert_int_eq(result, BLACKLISTED); + + result = remote_address("1.1.1.1", "999.999.999.999, 8.8.8.8, 12.23.45.67", address); + ck_assert_int_eq(result, BLACKLISTED); } END_TEST