Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions plugins/header_rewrite/Examples/Geo
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %${GEO:COUNTRY} =US
set-header ATS-Geo-Country %{GEO:COUNTRY}
set-header ATS-Geo-Country-ISO %{GEO:COUNTRY-ISO}
set-header ATS-Geo-ASN %{GEO:ASN}
set-header ATS-Geo-ASN-NAME %{GEO:ASN-NAME}
3 changes: 2 additions & 1 deletion plugins/header_rewrite/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ header_rewrite_la_SOURCES = \
resources.cc \
ruleset.cc \
statement.cc

header_rewrite_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)
header_rewrite_la_LIBADD = $(GEOIP_LIBS)

bin_PROGRAMS = header_rewrite_test
header_rewrite_test_SOURCES = parser.cc header_rewrite_test.cc
Expand Down
214 changes: 214 additions & 0 deletions plugins/header_rewrite/conditions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <sys/time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sstream>

#include "ts/ts.h"
Expand Down Expand Up @@ -709,3 +710,216 @@ ConditionNow::eval(const Resources &res)

return static_cast<const Matchers<int64_t> *>(_matcher)->test(now);
}


// ConditionGeo: Geo-based information (integer). See ConditionGeoCountry for the string version.
const char *
ConditionGeo::get_geo_string(const sockaddr *addr)
{
const char *ret = NULL;
int v = 4;

switch (_geo_qual) {
// Country database
case GEO_QUAL_COUNTRY:
switch (addr->sa_family) {
case AF_INET:
if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);

ret = GeoIP_country_code_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
}
break;
case AF_INET6: {
if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;

v = 6;
ret = GeoIP_country_code_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
}
} break;
default:
break;
}
TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country: %s", v, ret);
break;

// ASN database
case GEO_QUAL_ASN_NAME:
switch (addr->sa_family) {
case AF_INET:
if (gGeoIP[GEOIP_ASNUM_EDITION]) {
uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);

ret = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
}
break;
case AF_INET6: {
if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;

v = 6;
ret = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
}
} break;
default:
break;
}
TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN Name: %s", v, ret);
break;

default:
break;
}

return ret ? ret : "(unknown)";
}

int64_t
ConditionGeo::get_geo_int(const sockaddr *addr)
{
int64_t ret = -1;
int v = 4;

switch (_geo_qual) {
// Country Databse
case GEO_QUAL_COUNTRY_ISO:
switch (addr->sa_family) {
case AF_INET:
if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);

ret = GeoIP_id_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
}
break;
case AF_INET6: {
if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;

v = 6;
ret = GeoIP_id_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
}
} break;
default:
break;
}
TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country ISO: %" PRId64, v, ret);
break;

case GEO_QUAL_ASN: {
const char *asn_name = NULL;

switch (addr->sa_family) {
case AF_INET:
if (gGeoIP[GEOIP_ASNUM_EDITION]) {
uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);

asn_name = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
}
break;
case AF_INET6:
if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;

v = 6;
asn_name = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
}
break;
}
if (asn_name) {
// This is a little odd, but the strings returned are e.g. "AS1234 Acme Inc"
while (*asn_name && !(isdigit(*asn_name))) {
++asn_name;
}
ret = strtol(asn_name, NULL, 10);
}
}
TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN #: %" PRId64, v, ret);
break;

// Likely shouldn't trip, should we assert?
default:
break;
}

return ret;
}

void
ConditionGeo::initialize(Parser &p)
{
Condition::initialize(p);

if (is_int_type()) {
Matchers<int64_t> *match = new Matchers<int64_t>(_cond_op);

match->set(static_cast<int64_t>(strtol(p.get_arg().c_str(), NULL, 10)));
_matcher = match;
} else {
// The default is to have a string matcher
Matchers<std::string> *match = new Matchers<std::string>(_cond_op);

match->set(p.get_arg());
_matcher = match;
}
}

void
ConditionGeo::set_qualifier(const std::string &q)
{
Condition::set_qualifier(q);

TSDebug(PLUGIN_NAME, "\tParsing %%{GEO:%s} qualifier", q.c_str());

if (q == "COUNTRY") {
_geo_qual = GEO_QUAL_COUNTRY;
is_int_type(false);
} else if (q == "COUNTRY-ISO") {
_geo_qual = GEO_QUAL_COUNTRY_ISO;
is_int_type(true);
} else if (q == "ASN") {
_geo_qual = GEO_QUAL_ASN;
is_int_type(true);
} else if (q == "ASN-NAME") {
_geo_qual = GEO_QUAL_ASN_NAME;
is_int_type(false);
} else {
TSError("[%s] Unknown Geo qualifier: %s", PLUGIN_NAME, q.c_str());
}
}

void
ConditionGeo::append_value(std::string &s, const Resources &res)
{
std::ostringstream oss;

if (is_int_type()) {
oss << get_geo_int(TSHttpTxnClientAddrGet(res.txnp));
s += oss.str();
TSDebug(PLUGIN_NAME, "Appending GEO() to evaluation value -> %s", s.c_str());
} else {
oss << get_geo_string(TSHttpTxnClientAddrGet(res.txnp));
s += oss.str();
TSDebug(PLUGIN_NAME, "Appending GEO() to evaluation value -> %s", s.c_str());
}
}

bool
ConditionGeo::eval(const Resources &res)
{
if (is_int_type()) {
int64_t geo = get_geo_int(TSHttpTxnClientAddrGet(res.txnp));

TSDebug(PLUGIN_NAME, "Evaluating GEO() -> %" PRId64, geo);

return static_cast<const Matchers<int64_t> *>(_matcher)->test(geo);
} else {
std::string s;

append_value(s, res);
bool rval = static_cast<const Matchers<std::string> *>(_matcher)->test(s);

TSDebug(PLUGIN_NAME, "Evaluating GEO(): %s - rval: %d", s.c_str(), rval);
return rval;
}
}
43 changes: 43 additions & 0 deletions plugins/header_rewrite/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ class ConditionCookie : public Condition
};
};


// header
class ConditionHeader : public Condition
{
Expand All @@ -241,6 +242,7 @@ class ConditionHeader : public Condition
bool _client;
};


// path
class ConditionPath : public Condition
{
Expand Down Expand Up @@ -374,6 +376,7 @@ class ConditionIncomingPort : public Condition
DISALLOW_COPY_AND_ASSIGN(ConditionIncomingPort);
};


// Transact Count
class ConditionTransactCount : public Condition
{
Expand All @@ -392,6 +395,7 @@ class ConditionTransactCount : public Condition
DISALLOW_COPY_AND_ASSIGN(ConditionTransactCount);
};


// now: Keeping track of current time / day / hour etc.
class ConditionNow : public Condition
{
Expand All @@ -411,4 +415,43 @@ class ConditionNow : public Condition
};


// GeoIP class for the "integer" based Geo information pieces
class ConditionGeo : public Condition
{
public:
explicit ConditionGeo() : _geo_qual(GEO_QUAL_COUNTRY), _int_type(false)
{
TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionGeo");
};

void initialize(Parser &p);
void set_qualifier(const std::string &q);
void append_value(std::string &s, const Resources &res);

// These are special for this sub-class
bool
is_int_type() const
{
return _int_type;
}
void
is_int_type(bool flag)
{
_int_type = flag;
}

int64_t get_geo_int(const sockaddr *addr);
const char *get_geo_string(const sockaddr *addr);

protected:
bool eval(const Resources &res);

private:
DISALLOW_COPY_AND_ASSIGN(ConditionGeo);

GeoQualifiers _geo_qual;
bool _int_type;
};


#endif // __CONDITIONS_H
2 changes: 2 additions & 0 deletions plugins/header_rewrite/factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ condition_factory(const std::string &cond)
c = new ConditionTransactCount();
} else if (c_name == "NOW") {
c = new ConditionNow();
} else if (c_name == "GEO") {
c = new ConditionGeo();
} else {
TSError("[%s] Unknown condition: %s", PLUGIN_NAME, c_name.c_str());
return NULL;
Expand Down
34 changes: 34 additions & 0 deletions plugins/header_rewrite/header_rewrite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,40 @@
const char PLUGIN_NAME[] = "header_rewrite";
const char PLUGIN_NAME_DBG[] = "dbg_header_rewrite";


// Geo information, currently only Maxmind. These have to be initialized when the plugin loads.
#if HAVE_GEOIP_H
#include <GeoIP.h>

GeoIP *gGeoIP[NUM_DB_TYPES];

static void
initGeoIP()
{
GeoIPDBTypes dbs[] = {GEOIP_COUNTRY_EDITION, GEOIP_COUNTRY_EDITION_V6, GEOIP_ASNUM_EDITION, GEOIP_ASNUM_EDITION_V6};

for (unsigned i = 0; i < sizeof(dbs) / sizeof(dbs[0]); ++i) {
if (!gGeoIP[dbs[i]] && GeoIP_db_avail(dbs[i])) {
// GEOIP_STANDARD seems to break threaded apps...
gGeoIP[dbs[i]] = GeoIP_open_type(dbs[i], GEOIP_MMAP_CACHE);
TSDebug(PLUGIN_NAME, "initialized GeoIP-DB[%d] %s", dbs[i], GeoIP_database_info(gGeoIP[dbs[i]]));
}
}
}

#else

static void
initGeoIP()
{
}
#endif


// Forward declaration for the main continuation.
static int cont_rewrite_headers(TSCont, TSEvent, void *);


// Simple wrapper around a configuration file / set. This is useful such that
// we can reuse most of the code for both global and per-remap rule sets.
class RulesConfig
Expand Down Expand Up @@ -304,6 +335,7 @@ TSPluginInit(int argc, const char *argv[])
RulesConfig *conf = new RulesConfig;
bool got_config = false;

initGeoIP();
conf->hold();

for (int i = 1; i < argc; ++i) {
Expand Down Expand Up @@ -358,7 +390,9 @@ TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
return TS_ERROR;
}

initGeoIP();
TSDebug(PLUGIN_NAME, "Remap plugin is successfully initialized");

return TS_SUCCESS;
}

Expand Down
1 change: 1 addition & 0 deletions plugins/header_rewrite/resources.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "resources.h"
#include "lulu.h"


// Collect all resources
void
Resources::gather(const ResourceIDs ids, TSHttpHookID hook)
Expand Down
Loading