Skip to content
This repository has been archived by the owner on Aug 4, 2019. It is now read-only.

Commit

Permalink
Refactor xff parsing.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
abedra committed Apr 8, 2015
1 parent 2d1f344 commit 38745b9
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/repsheet.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
58 changes: 36 additions & 22 deletions src/xff.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <pcre.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

#include "repsheet.h"
#include "xff.h"

/**
Expand All @@ -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;
}
2 changes: 1 addition & 1 deletion src/xff.h
Original file line number Diff line number Diff line change
@@ -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
28 changes: 22 additions & 6 deletions test/xff_test.c
Original file line number Diff line number Diff line change
@@ -1,28 +1,44 @@
#include <arpa/inet.h>
#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

Expand Down

0 comments on commit 38745b9

Please sign in to comment.