Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge remote-tracking branch 'origin/master' into HEAD

Conflicts:
	Changes
	README
	mod_geoip.c
  • Loading branch information...
commit a0129a6f9e74da2eb481b350d31771f0ed43cb37 2 parents 0991b01 + 54f11fc
Rod Vagg authored

Showing 4 changed files with 188 additions and 8 deletions. Show diff stats Hide diff stats

  1. +4 0 .gitignore
  2. +9 0 Changes
  3. +46 0 README.md
  4. +129 8 mod_geoip.c
4 .gitignore
... ... @@ -0,0 +1,4 @@
  1 +mod_geoip.la
  2 +mod_geoip.lo
  3 +mod_geoip.slo
  4 +.libs
9 Changes
... ... @@ -1,6 +1,15 @@
1 1 - Support Aapche 2.4 ( Boris Zentner )
2 2 - Use GeoIP_id_by_addr_v6 instead of GeoIP_country_id_by_addr_v6 ( Boris Zentner )
3 3 - Include util_script.h to silence warning about ap_add_common_vars ( Boris Zentner )
  4 +1.2.7-RV4 Aug 29th 2012
  5 + - bug fix from @KevinGaudin, bad string length allocation for matches, see PR #2
  6 +1.2.7-RV3 Jul 7th 2012
  7 + - Introduced GeoIPEnableHostnameLookups config option to do name lookups on the X-Forwarded-For match, result is stored in GEOIP_HOST environment variable (useful for logging amongst other things) (Rod Vagg)
  8 +1.2.7-RV2 Jul 6th 2012
  9 + - Added fallback to remote_ip where X-Forwarded-For handler fails to find a non-private match (Kevin Gaudin)
  10 + - Introduced GeoIPUseLeftPublicXForwardedForIP config option to switch to new X-Forwarded-For handler, left GeoIPUseLastXForwardedForIP and default option in place and still active (Rod Vagg)
  11 +1.2.7-RV1 Apr 22nd 2012
  12 + - Added proper X-Forwarded-For handling, see http://rod.vagg.org/2012/04/a-mod_geoip2-that-properly-handles-x-forwarded-for/ (Rod Vagg)
4 13 1.2.7 Aug 23th 2011
5 14 - Add support for GEOIP_NETSPEED_REV1 ( Boris Zentner )
6 15 - Experimental support for GEOIP_COUNTRY_EDITION_V6 ( Boris Zentner )
46 README.md
Source Rendered
... ... @@ -0,0 +1,46 @@
  1 +# mod_geoip2_xff
  2 +
  3 +**mod_geoip2 with fancy, new-fangled X-Forwarded-For handling**
  4 +---------------------
  5 +
  6 +A fork of [MaxMind](http://www.maxmind.com)'s
  7 +[mod_geoip2](http://www.maxmind.com/app/mod_geoip) with more intelligent
  8 +X-Forwarded-For handling, as per
  9 +[this post](http://rod.vagg.org/2011/07/wrangling-the-x-forwarded-for-header/).
  10 +
  11 +Currently based on mod_geoip2_1.2.7
  12 +
  13 +**Summary**: The client-IP address in the list of addresses potentially
  14 +in the X-Forwarded-For HTTP header is normally the left-most header but
  15 +clients with private IP addresses using a forwarding proxy mean that
  16 +you'll often get the wrong address, usually a private IP address. This
  17 +module follows the rule:
  18 +
  19 +**Always use the leftmost non-private address.**
  20 +
  21 +You can compare with the current MaxMind release
  22 +[here](https://github.com/rvagg/mod_geoip2_xff/compare/maxmind...master).
  23 +
  24 +To enable this special mode you need to turn the **GeoIPUseLeftPublicXForwardedForIP**
  25 +flag **On** in your apache config.
  26 +
  27 +An additional config option is also available: **GeoIPEnableHostnameLookups**, when
  28 +set to **On** mod_geoip2 will attempt to resolve the matched IP address and store
  29 +the result in the GEOIP_HOST environment variable. This is useful for logging or
  30 +other basic remote host based configuration.
  31 +
  32 +Changes for this fork are being recorded in the
  33 +[Changes](https://github.com/rvagg/mod_geoip2_xff/blob/master/Changes) file.
  34 +
  35 +Contributions more than welcome!
  36 +
  37 +Thanks to Kevin Gaudin for his contributions to fall back to the RemoteIP
  38 +where we fail to find a public IP address match.
  39 +
  40 +Original MaxMind README is
  41 +[here](https://github.com/rvagg/mod_geoip2_xff/blob/master/README).
  42 +
  43 +Follow the same instructions found in the original
  44 +[INSTALL](https://github.com/rvagg/mod_geoip2_xff/blob/master/INSTALL).
  45 +
  46 +**See [Downloads](https://github.com/rvagg/mod_geoip2_xff/downloads) for tarballs.**
137 mod_geoip.c
@@ -60,6 +60,12 @@
60 60 *
61 61 * Initial port Contributed by Corris Randall <corris@cpan.org>
62 62 *
  63 + * Improved X-Forwarded-For handling by Rod Vagg <rod@vagg.org>
  64 + * http://rod.vagg.org/2012/04/a-mod_geoip2-that-properly-handles-x-forwarded-for/
  65 + * (GeoIPUseLeftPublicXForwardedForIP)
  66 + *
  67 + * Additional contributions by Kevin Gaudin <kevin.gaudin@gmail.com>
  68 + *
63 69 */
64 70
65 71 #include "httpd.h"
@@ -71,6 +77,9 @@
71 77 #include "util_script.h"
72 78 #include <GeoIP.h>
73 79 #include <GeoIPCity.h>
  80 +#include <regex.h>
  81 +#include <arpa/inet.h>
  82 +#include <netdb.h>
74 83
75 84 typedef struct {
76 85 int GeoIPEnabled;
@@ -82,11 +91,13 @@ typedef struct {
82 91 char **GeoIPFilenames;
83 92 int GeoIPEnabled;
84 93 int GeoIPEnableUTF8;
  94 + int GeoIPEnableHostnameLookups;
85 95 char GeoIPOutput;
86 96 int GeoIPFlags;
87 97 int *GeoIPFlags2;
88 98 int scanProxyHeaders;
89   - int use_last_x_forwarded_for_ip;
  99 + int use_last_x_forwarded_for_ip;
  100 + int use_left_public_x_forwarded_for_ip;
90 101 } geoip_server_config_rec;
91 102
92 103 static const int GEOIP_NONE = 0;
@@ -134,11 +145,13 @@ static void *create_geoip_server_config( apr_pool_t *p, server_rec *d )
134 145 conf->GeoIPFilenames = NULL;
135 146 conf->GeoIPEnabled = 0;
136 147 conf->GeoIPEnableUTF8 = 0;
  148 + conf->GeoIPEnableHostnameLookups = 0;
137 149 conf->GeoIPOutput = GEOIP_INIT;
138 150 conf->GeoIPFlags = GEOIP_STANDARD;
139 151 conf->GeoIPFlags2 = NULL;
140 152 conf->scanProxyHeaders = 0;
141   - conf->use_last_x_forwarded_for_ip = 0;
  153 + conf->use_last_x_forwarded_for_ip = 0;
  154 + conf->use_left_public_x_forwarded_for_ip = 0;
142 155 return (void *)conf;
143 156 }
144 157
@@ -294,6 +307,59 @@ geoip_per_dir(request_rec * r)
294 307 return geoip_header_parser(r);
295 308 }
296 309
  310 +char* ipregex = "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})";
  311 +char* privipregex = "(^127\\.0\\.0\\.1)|(^10\\.)|(^172\\.1[6-9]\\.)|(^172\\.2[0-9]\\.)|(^172\\.3[0-1]\\.)|(^192\\.168\\.)";
  312 +int ipregexok = -1;
  313 +regex_t ipcompiledregex;
  314 +regex_t privipcompiledregex;
  315 +
  316 +char* first_public_ip_in_list(char* instr, char* remote_ip) {
  317 + if (ipregexok = -1)
  318 + ipregexok = (regcomp(&ipcompiledregex, ipregex, REG_EXTENDED) == 0 && regcomp(&privipcompiledregex, privipregex, REG_EXTENDED) == 0) ? 1 : 0;
  319 +
  320 + char* match = 0;
  321 + if (ipregexok) {
  322 + int len = sizeof(char) * strlen(instr) + 1;
  323 + int matched = 0;
  324 + char* str = malloc(len);
  325 + strcpy(str, instr);
  326 + char* tmp = malloc(len);
  327 + regmatch_t* matches = malloc(sizeof(regmatch_t));
  328 + regmatch_t* privmatches = malloc(sizeof(regmatch_t));
  329 + while (regexec(&ipcompiledregex, str, 1, matches, 0) == 0) {
  330 + int eo = matches[0].rm_eo;
  331 + if (matches[0].rm_so > -1){
  332 + len = eo - matches[0].rm_so;
  333 + match = malloc(len + 1);
  334 + strncpy(match, str + matches[0].rm_so, len);
  335 + match[len] = 0;
  336 + if (regexec(&privipcompiledregex, match, 1, privmatches, 0) != 0 || privmatches[0].rm_so < 0) {
  337 + break;
  338 + }
  339 +
  340 + free(match);
  341 + match = 0;
  342 + }
  343 + if (eo < strlen(str)) {
  344 + strcpy(tmp, str + eo);
  345 + strcpy(str, tmp);
  346 + } else
  347 + break;
  348 + }
  349 + free(tmp);
  350 + free(str);
  351 + free(matches);
  352 + free(privmatches);
  353 + }
  354 + if (!match) {
  355 + // We did not find any public IP in the X-Forwarded-For chain...
  356 + // let's use the remote_ip as our last chance as we won't get any result from XFF IPs anyway.
  357 + int len = strlen(remote_ip) + 1;
  358 + match = malloc(len);
  359 + strncpy(match, remote_ip, len);
  360 + }
  361 + return match;
  362 +}
297 363
298 364 static int
299 365 geoip_header_parser(request_rec * r)
@@ -317,6 +383,10 @@ geoip_header_parser(request_rec * r)
317 383 /* For splitting proxy headers */
318 384 char *ipaddr_ptr = 0;
319 385 char *comma_ptr;
  386 + char *found_ip;
  387 + apr_sockaddr_t *sa;
  388 + char *hostname = 0;
  389 +
320 390 cfg = ap_get_module_config(r->server->module_config, &geoip_module);
321 391
322 392 if (!cfg)
@@ -353,6 +423,14 @@ geoip_header_parser(request_rec * r)
353 423 }
354 424 else {
355 425 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server, "[mod_geoip]: IPADDR_PTR: %s", ipaddr_ptr);
  426 +
  427 + if (cfg->use_left_public_x_forwarded_for_ip) {
  428 + // find the first public IP address in a potentially comma-separated list,
  429 + // fall back to remote_ip if we can't find one
  430 + ipaddr = first_public_ip_in_list(ipaddr_ptr, r->connection->remote_ip);
  431 + } else {
  432 + // leaving some of the following inconsistent indenting intact for easier diff to original maxmind src
  433 +
356 434 /*
357 435 * Check to ensure that the HTTP_CLIENT_IP or
358 436 * X-Forwarded-For header is not a comma separated
@@ -375,6 +453,7 @@ geoip_header_parser(request_rec * r)
375 453 comma_ptr = strchr(ipaddr, ',');
376 454 if (comma_ptr != 0)
377 455 *comma_ptr = '\0';
  456 + }
378 457 }
379 458 }
380 459
@@ -409,12 +488,23 @@ geoip_header_parser(request_rec * r)
409 488 }
410 489 #endif
411 490
412   - if (cfg->GeoIPOutput & GEOIP_NOTES) {
413   - apr_table_setn(r->notes, "GEOIP_ADDR", ipaddr);
414   - }
415   - if (cfg->GeoIPOutput & GEOIP_ENV) {
416   - apr_table_setn(r->subprocess_env, "GEOIP_ADDR", ipaddr);
417   - }
  491 + if (cfg->GeoIPEnableHostnameLookups
  492 + && apr_sockaddr_info_get(&sa, ipaddr, APR_INET, 0, 0, r->pool) == APR_SUCCESS
  493 + && apr_getnameinfo(&hostname, sa, 0) == APR_SUCCESS) {
  494 + ap_str_tolower(hostname);
  495 + }
  496 +
  497 + if (!hostname)
  498 + hostname = ipaddr;
  499 +
  500 + if (cfg->GeoIPOutput & GEOIP_NOTES) {
  501 + apr_table_setn(r->notes, "GEOIP_ADDR", ipaddr);
  502 + apr_table_setn(r->notes, "GEOIP_HOST", hostname);
  503 + }
  504 + if (cfg->GeoIPOutput & GEOIP_ENV) {
  505 + apr_table_setn(r->subprocess_env, "GEOIP_ADDR", ipaddr);
  506 + apr_table_setn(r->subprocess_env, "GEOIP_HOST", hostname);
  507 + }
418 508
419 509 for (i = 0; i < cfg->numGeoIPFiles; i++) {
420 510
@@ -739,6 +829,17 @@ static const char *geoip_use_last_x_forwarded_for_ip(cmd_parms *cmd, void *dummy
739 829 conf->use_last_x_forwarded_for_ip = arg;
740 830 return NULL;
741 831 }
  832 +static const char *geoip_use_left_public_x_forwarded_for_ip(cmd_parms *cmd, void *dummy, int arg)
  833 +{
  834 + geoip_server_config_rec *conf = (geoip_server_config_rec *)
  835 + ap_get_module_config(cmd->server->module_config, &geoip_module);
  836 +
  837 + if (!conf)
  838 + return "mod_geoip: server structure not allocated";
  839 +
  840 + conf->use_left_public_x_forwarded_for_ip = arg;
  841 + return NULL;
  842 +}
742 843 static const char *geoip_scanproxy(cmd_parms *cmd, void *dummy, int arg)
743 844 {
744 845 geoip_server_config_rec *conf = (geoip_server_config_rec *)
@@ -785,6 +886,17 @@ static const char *set_geoip_enable_utf8(cmd_parms *cmd, void *dummy, int arg)
785 886 return NULL;
786 887 }
787 888
  889 +static const char *set_geoip_enable_hostname(cmd_parms *cmd, void *dummy, int arg)
  890 +{
  891 + geoip_server_config_rec *conf = (geoip_server_config_rec *)
  892 + ap_get_module_config(cmd->server->module_config, &geoip_module);
  893 +
  894 + if (!conf)
  895 + return "mod_geoip: server structure not allocated";
  896 +
  897 + conf->GeoIPEnableHostnameLookups = arg;
  898 + return NULL;
  899 +}
788 900
789 901 static const char *set_geoip_filename(cmd_parms *cmd, void *dummy, const char *filename,const char *arg2)
790 902 {
@@ -843,6 +955,7 @@ static void *make_geoip(apr_pool_t *p, server_rec *d)
843 955 dcfg->GeoIPFilenames = NULL;
844 956 dcfg->GeoIPEnabled = 0;
845 957 dcfg->GeoIPEnableUTF8 = 0;
  958 + dcfg->GeoIPEnableHostnameLookups = 0;
846 959 dcfg->GeoIPOutput = GEOIP_INIT;
847 960 dcfg->GeoIPFlags = GEOIP_STANDARD;
848 961 dcfg->GeoIPFlags2 = NULL;
@@ -854,8 +967,16 @@ static const command_rec geoip_cmds[] =
854 967 {
855 968 AP_INIT_FLAG("GeoIPScanProxyHeaders", geoip_scanproxy, NULL, RSRC_CONF, "Get IP from HTTP_CLIENT IP or X-Forwarded-For"),
856 969 AP_INIT_FLAG("GeoIPUseLastXForwardedForIP", geoip_use_last_x_forwarded_for_ip, NULL, RSRC_CONF, "For more IP's in X-Forwarded-For, use the last"),
  970 + AP_INIT_FLAG(
  971 + "GeoIPUseLeftPublicXForwardedForIP"
  972 + , geoip_use_left_public_x_forwarded_for_ip
  973 + , NULL
  974 + , RSRC_CONF
  975 + , "For more IP's in X-Forwarded-For, use the leftmost non-private address"
  976 + ),
857 977 AP_INIT_FLAG("GeoIPEnable", set_geoip_enable, NULL, RSRC_CONF | OR_FILEINFO, "Turn on mod_geoip"),
858 978 AP_INIT_FLAG("GeoIPEnableUTF8", set_geoip_enable_utf8, NULL, RSRC_CONF, "Turn on utf8 characters for city names"),
  979 + AP_INIT_FLAG("GeoIPEnableHostnameLookups", set_geoip_enable_hostname, NULL, RSRC_CONF, "Turn on hostname lookups for GEOIP_HOST"),
859 980 AP_INIT_TAKE12("GeoIPDBFile", set_geoip_filename, NULL, RSRC_CONF, "Path to GeoIP Data File"),
860 981 AP_INIT_ITERATE("GeoIPOutput", set_geoip_output, NULL, RSRC_CONF, "Specify output method(s)"),
861 982 {NULL}

0 comments on commit a0129a6

Please sign in to comment.
Something went wrong with that request. Please try again.