header_rewrite: allow for use of maxminddb as source of geo truth (#7695)
diff --git a/configure.ac b/configure.ac
index 2699734..514fc5a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1685,12 +1685,16 @@
#
AC_CHECK_HEADERS([GeoIP.h], [
AC_CHECK_LIB([GeoIP], [GeoIP_new], [
- AC_SUBST([GEO_LIBS], ["-lGeoIP"])
+ AC_SUBST([GEOIP_LIBS], ["-lGeoIP"])
+ AC_SUBST(has_geoip, 1)
], [
- AC_SUBST([GEO_LIBS], [""])
+ AC_SUBST([GEOIP_LIBS], [""])
+ AC_SUBST(has_geoip, 0)
])
])
+AM_CONDITIONAL([HAS_GEOIP], [test "x${has_geoip}" = "x1" ])
+
#
# Check for libmaxmind. This is the maxmind v2 API where GeoIP is the legacy
# v1 dat file based API
@@ -1698,14 +1702,49 @@
AC_CHECK_HEADERS([maxminddb.h], [
AC_CHECK_LIB([maxminddb], [MMDB_open], [
AC_SUBST([MAXMINDDB_LIBS], ["-lmaxminddb"])
- AC_SUBST(has_maxmind, 1)
+ AC_SUBST(has_maxminddb, 1)
], [
AC_SUBST([MAXMINDDB_LIBS], [""])
- AC_SUBST(has_maxmind, 0)
+ AC_SUBST(has_maxminddb, 0)
])
])
-AM_CONDITIONAL([BUILD_MAXMIND_ACL_PLUGIN], [test "x${has_maxmind}" = "x1" ])
+AM_CONDITIONAL([HAS_MAXMINDDB], [test "x${has_maxminddb}" = "x1" ])
+
+AC_ARG_WITH([hrw-geo-provider],
+ [AS_HELP_STRING([--with-hrw-geo-provider=geoip|maxminddb],[geo provider to use with header_rewrite [default=auto] ])],
+ [geo_provider=$withval],
+ [geo_provider="auto"]
+)
+use_hrw_geoip=0
+use_hrw_maxminddb=0
+
+AS_IF([test "x$geo_provider" = "xauto"], [
+ if test "x$has_geoip" = "x1"; then
+ use_hrw_geoip=1
+ AC_MSG_NOTICE([Using GeoIP interface for header_rewrite])
+ elif test "x$has_maxminddb" = "x1"; then
+ use_hrw_maxminddb=1
+ AC_MSG_NOTICE([Using MaxMindDB interface for header_rewrite])
+ fi
+],[
+ case "x$geo_provider" in
+ xgeoip)
+ use_hrw_geoip=1
+ AC_MSG_RESULT([forced to GeoIP])
+ ;;
+ xmaxminddb)
+ use_hrw_maxminddb=1
+ AC_MSG_RESULT([forced to MaxMindDB])
+ ;;
+ *)
+ AC_MSG_RESULT([failed])
+ AC_MSG_FAILURE([unknown geo interface $geo_provider])
+ esac
+])
+
+AC_SUBST(use_hrw_geoip)
+AC_SUBST(use_hrw_maxminddb)
# Right now, the healthcheck plugins requires inotify_init (and friends)
AM_CONDITIONAL([BUILD_HEALTHCHECK_PLUGIN], [ test "$ac_cv_func_inotify_init" = "yes" ])
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 40f86a3..5caa270 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -82,11 +82,16 @@
rewriting rules are evaluated for every request made to your |TS| instance.
This is done by adding the following line to your :file:`plugin.config`::
- header_rewrite.so config_file_1.conf config_file_2.conf ...
+ header_rewrite.so [--geo-db-path=path/to/geoip.db] config_file_1.conf config_file_2.conf ...
You may specify multiple configuration files. Their rules will be evaluated in
the order the files are listed.
+The plugin takes an optional switch ``--geo-db-path``. If MaxMindDB support has
+been compiled in, use this switch to point at your .mmdb file. This also applies to
+the remap context.
+
+
Enabling Per-Mapping
--------------------
@@ -219,7 +224,7 @@
cond %{GEO:<part>} <operand>
Perform a GeoIP lookup of the client-IP, using a 3rd party library and
-DB. Currently only the MaxMind GeoIP API is supported. The default is to
+DB. Currently the MaxMind GeoIP and MaxMindDB APIs are supported. The default is to
do a Country lookup, but the following qualifiers are supported::
%{GEO:COUNTRY} The country code (e.g. "US")
@@ -495,7 +500,7 @@
~~~~~~~~
::
- cond %{<name>}
+ cond %{<name>}
add-header @PropertyName "%{TCP-INFO}"
This operation records TCP Info struct field values as an Internal remap as well as global header at the event hook specified by the condition. Supported hook conditions include TXN_START_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Global plugin and REMAP_PSEUDO_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Remap plugin. Conditions supported as request headers include TXN_START_HOOK and REMAP_PSEUDO_HOOK. The other conditions are supported as response headers. TCP Info fields currently recorded include rtt, rto, snd_cwnd and all_retrans. This operation is not supported on transactions originated within Traffic Server (for e.g using the |TS| :c:func:`TSHttpTxnIsInternal`)
diff --git a/include/tscore/ink_config.h.in b/include/tscore/ink_config.h.in
index 8b80172..c23417f 100644
--- a/include/tscore/ink_config.h.in
+++ b/include/tscore/ink_config.h.in
@@ -81,6 +81,9 @@
#define TS_HAS_TLS_EARLY_DATA @has_tls_early_data@
#define TS_HAS_TLS_SESSION_TICKET @has_tls_session_ticket@
+#define TS_USE_HRW_GEOIP @use_hrw_geoip@
+#define TS_USE_HRW_MAXMINDDB @use_hrw_maxminddb@
+
#define TS_HAS_SO_PEERCRED @has_so_peercred@
/* OS API definitions */
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index b739a05..76addc5 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -39,7 +39,9 @@
include generator/Makefile.inc
include compress/Makefile.inc
include header_rewrite/Makefile.inc
+if BUILD_HEALTHCHECK_PLUGIN
include healthchecks/Makefile.inc
+endif
include libloader/Makefile.inc
if HAS_LUAJIT
include lua/Makefile.inc
@@ -71,7 +73,7 @@
include experimental/icap/Makefile.inc
include experimental/inliner/Makefile.inc
-if BUILD_MAXMIND_ACL_PLUGIN
+if HAS_MAXMINDDB
include experimental/maxmind_acl/Makefile.inc
endif
diff --git a/plugins/experimental/geoip_acl/Makefile.inc b/plugins/experimental/geoip_acl/Makefile.inc
index 3ff84f6..150b3c3 100644
--- a/plugins/experimental/geoip_acl/Makefile.inc
+++ b/plugins/experimental/geoip_acl/Makefile.inc
@@ -20,4 +20,4 @@
experimental/geoip_acl/acl.cc \
experimental/geoip_acl/geoip_acl.cc
-experimental_geoip_acl_geoip_acl_la_LIBADD = $(GEO_LIBS)
+experimental_geoip_acl_geoip_acl_la_LIBADD = $(GEOIP_LIBS)
diff --git a/plugins/header_rewrite/Makefile.inc b/plugins/header_rewrite/Makefile.inc
index 0b5cfe8..a64fffa 100644
--- a/plugins/header_rewrite/Makefile.inc
+++ b/plugins/header_rewrite/Makefile.inc
@@ -44,16 +44,38 @@
header_rewrite/value.cc \
header_rewrite/value.h
+if HAS_MAXMINDDB
+header_rewrite_header_rewrite_la_SOURCES += header_rewrite/conditions_geo_maxmind.cc
+endif
+
+if HAS_GEOIP
+header_rewrite_header_rewrite_la_SOURCES += header_rewrite/conditions_geo_geoip.cc
+endif
+
header_rewrite_parser_la_SOURCES = \
header_rewrite/parser.cc \
header_rewrite/parser.h
header_rewrite_header_rewrite_la_LIBADD = \
- header_rewrite/parser.la \
- $(GEO_LIBS)
+ header_rewrite/parser.la
+
+if HAS_GEOIP
+header_rewrite_header_rewrite_la_LIBADD += $(GEOIP_LIBS)
+endif
+
+if HAS_MAXMINDDB
+header_rewrite_header_rewrite_la_LIBADD += $(MAXMINDDB_LIBS)
+endif
check_PROGRAMS += header_rewrite/header_rewrite_test
header_rewrite_header_rewrite_test_SOURCES = \
header_rewrite/header_rewrite_test.cc
header_rewrite_header_rewrite_test_LDADD = \
header_rewrite/parser.la
+if HAS_GEOIP
+header_rewrite_header_rewrite_test_LDADD += $(GEOIP_LIBS)
+endif
+
+if HAS_MAXMINDDB
+header_rewrite_header_rewrite_test_LDADD += $(MAXMINDDB_LIBS)
+endif
diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc
index 66c3e5d..f88dadc 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -15,10 +15,12 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
+
//////////////////////////////////////////////////////////////////////////////////////////////
// conditions.cc: Implementation of the condition classes
//
//
+
#include <sys/time.h>
#include <unistd.h>
#include <arpa/inet.h>
@@ -740,155 +742,11 @@
return static_cast<const MatcherType *>(_matcher)->test(now);
}
-// ConditionGeo: Geo-based information (integer). See ConditionGeoCountry for the string version.
-#if HAVE_GEOIP_H
-const char *
-ConditionGeo::get_geo_string(const sockaddr *addr) const
-{
- const char *ret = "(unknown)";
- int v = 4;
-
- if (addr) {
- 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) const
-{
- int64_t ret = -1;
- int v = 4;
-
- if (!addr) {
- return 0;
- }
-
- switch (_geo_qual) {
- // Country Database
- 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 = nullptr;
-
- 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, nullptr, 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;
-}
-
-#else
-
-// No Geo library available, these are just stubs.
-
-const char *
+std::string
ConditionGeo::get_geo_string(const sockaddr *addr) const
{
TSError("[%s] No Geo library available!", PLUGIN_NAME);
- return nullptr;
+ return "";
}
int64_t
@@ -898,8 +756,6 @@
return 0;
}
-#endif
-
void
ConditionGeo::initialize(Parser &p)
{
diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h
index ed6c61f..64edb72 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -437,12 +437,12 @@
_int_type = flag;
}
+private:
+ virtual int64_t get_geo_int(const sockaddr *addr) const;
+ virtual std::string get_geo_string(const sockaddr *addr) const;
+
protected:
bool eval(const Resources &res) override;
-
-private:
- int64_t get_geo_int(const sockaddr *addr) const;
- const char *get_geo_string(const sockaddr *addr) const;
GeoQualifiers _geo_qual = GEO_QUAL_COUNTRY;
bool _int_type = false;
};
diff --git a/plugins/header_rewrite/conditions_geo.h b/plugins/header_rewrite/conditions_geo.h
new file mode 100644
index 0000000..2547716
--- /dev/null
+++ b/plugins/header_rewrite/conditions_geo.h
@@ -0,0 +1,46 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <string>
+
+#include "conditions.h"
+
+class MMConditionGeo : public ConditionGeo
+{
+public:
+ MMConditionGeo() {}
+ virtual ~MMConditionGeo() {}
+
+ static void initLibrary(const std::string &path);
+
+ virtual int64_t get_geo_int(const sockaddr *addr) const override;
+ virtual std::string get_geo_string(const sockaddr *addr) const override;
+};
+
+class GeoIPConditionGeo : public ConditionGeo
+{
+public:
+ GeoIPConditionGeo() {}
+ virtual ~GeoIPConditionGeo() {}
+
+ static void initLibrary(const std::string &path);
+
+ virtual int64_t get_geo_int(const sockaddr *addr) const override;
+ virtual std::string get_geo_string(const sockaddr *addr) const override;
+};
diff --git a/plugins/header_rewrite/conditions_geo_geoip.cc b/plugins/header_rewrite/conditions_geo_geoip.cc
new file mode 100644
index 0000000..f883023
--- /dev/null
+++ b/plugins/header_rewrite/conditions_geo_geoip.cc
@@ -0,0 +1,188 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// conditions_geo_geoip.cc: Implementation of the ConditionGeo class based on the GeoIP library
+//
+//
+
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <cctype>
+
+#include "ts/ts.h"
+
+#include "conditions_geo.h"
+
+#include <GeoIP.h>
+
+GeoIP *gGeoIP[NUM_DB_TYPES];
+
+void
+GeoIPConditionGeo::initLibrary(const std::string &)
+{
+ GeoIPDBTypes dbs[] = {GEOIP_COUNTRY_EDITION, GEOIP_COUNTRY_EDITION_V6, GEOIP_ASNUM_EDITION, GEOIP_ASNUM_EDITION_V6};
+
+ for (auto &db : dbs) {
+ if (!gGeoIP[db] && GeoIP_db_avail(db)) {
+ // GEOIP_STANDARD seems to break threaded apps...
+ gGeoIP[db] = GeoIP_open_type(db, GEOIP_MMAP_CACHE);
+
+ char *db_info = GeoIP_database_info(gGeoIP[db]);
+ TSDebug(PLUGIN_NAME, "initialized GeoIP-DB[%d] %s", db, db_info);
+ free(db_info);
+ }
+ }
+}
+
+std::string
+GeoIPConditionGeo::get_geo_string(const sockaddr *addr) const
+{
+ std::string ret = "(unknown)";
+ int v = 4;
+
+ if (addr) {
+ 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.c_str());
+ 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.c_str());
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int64_t
+GeoIPConditionGeo::get_geo_int(const sockaddr *addr) const
+{
+ int64_t ret = -1;
+ int v = 4;
+
+ if (!addr) {
+ return 0;
+ }
+
+ switch (_geo_qual) {
+ // Country Database
+ 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 = nullptr;
+
+ 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, nullptr, 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;
+}
diff --git a/plugins/header_rewrite/conditions_geo_maxmind.cc b/plugins/header_rewrite/conditions_geo_maxmind.cc
new file mode 100644
index 0000000..8438e1c
--- /dev/null
+++ b/plugins/header_rewrite/conditions_geo_maxmind.cc
@@ -0,0 +1,175 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// conditions_geo_maxmind.cc: Implementation of the ConditionGeo class based on MaxMindDB
+//
+//
+
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "ts/ts.h"
+
+#include "conditions_geo.h"
+
+#include <maxminddb.h>
+
+MMDB_s *gMaxMindDB = nullptr;
+
+void
+MMConditionGeo::initLibrary(const std::string &path)
+{
+ if (path.empty()) {
+ TSError("[%s] Empty db path specified. Not initializing!", PLUGIN_NAME);
+ return;
+ }
+ gMaxMindDB = new MMDB_s;
+
+ int status = MMDB_open(path.c_str(), MMDB_MODE_MMAP, gMaxMindDB);
+ if (MMDB_SUCCESS != status) {
+ TSDebug(PLUGIN_NAME, "Cannot open %s - %s", path.c_str(), MMDB_strerror(status));
+ return;
+ }
+ TSDebug(PLUGIN_NAME, "Loaded %s", path.c_str());
+}
+
+std::string
+MMConditionGeo::get_geo_string(const sockaddr *addr) const
+{
+ std::string ret = "(unknown)";
+ int mmdb_error;
+
+ if (gMaxMindDB == nullptr) {
+ return ret;
+ }
+
+ MMDB_lookup_result_s result = MMDB_lookup_sockaddr(gMaxMindDB, addr, &mmdb_error);
+
+ if (MMDB_SUCCESS != mmdb_error) {
+ TSDebug(PLUGIN_NAME, "Error during sockaddr lookup: %s", MMDB_strerror(mmdb_error));
+ return ret;
+ }
+
+ MMDB_entry_data_list_s *entry_data_list = nullptr;
+ if (!result.found_entry) {
+ TSDebug(PLUGIN_NAME, "No entry for this IP was found");
+ return ret;
+ }
+
+ int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
+ if (MMDB_SUCCESS != status) {
+ TSDebug(PLUGIN_NAME, "Error looking up entry data: %s", MMDB_strerror(status));
+ return ret;
+ }
+
+ if (entry_data_list == nullptr) {
+ TSDebug(PLUGIN_NAME, "No data found");
+ return ret;
+ }
+
+ const char *field_name;
+ switch (_geo_qual) {
+ case GEO_QUAL_COUNTRY:
+ field_name = "country_code";
+ break;
+ case GEO_QUAL_ASN_NAME:
+ field_name = "autonomous_system_organization";
+ break;
+ default:
+ TSDebug(PLUGIN_NAME, "Unsupported field %d", _geo_qual);
+ return ret;
+ break;
+ }
+
+ MMDB_entry_data_s entry_data;
+
+ status = MMDB_get_value(&result.entry, &entry_data, field_name, NULL);
+ if (MMDB_SUCCESS != status) {
+ TSDebug(PLUGIN_NAME, "ERROR on get value asn value: %s", MMDB_strerror(status));
+ return ret;
+ }
+ ret = std::string(entry_data.utf8_string, entry_data.data_size);
+
+ if (NULL != entry_data_list) {
+ MMDB_free_entry_data_list(entry_data_list);
+ }
+
+ return ret;
+}
+
+int64_t
+MMConditionGeo::get_geo_int(const sockaddr *addr) const
+{
+ int64_t ret = -1;
+ int mmdb_error;
+
+ if (gMaxMindDB == nullptr) {
+ return ret;
+ }
+
+ MMDB_lookup_result_s result = MMDB_lookup_sockaddr(gMaxMindDB, addr, &mmdb_error);
+
+ if (MMDB_SUCCESS != mmdb_error) {
+ TSDebug(PLUGIN_NAME, "Error during sockaddr lookup: %s", MMDB_strerror(mmdb_error));
+ return ret;
+ }
+
+ MMDB_entry_data_list_s *entry_data_list = nullptr;
+ if (!result.found_entry) {
+ TSDebug(PLUGIN_NAME, "No entry for this IP was found");
+ return ret;
+ }
+
+ int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
+ if (MMDB_SUCCESS != status) {
+ TSDebug(PLUGIN_NAME, "Error looking up entry data: %s", MMDB_strerror(status));
+ return ret;
+ }
+
+ if (entry_data_list == nullptr) {
+ TSDebug(PLUGIN_NAME, "No data found");
+ return ret;
+ }
+
+ const char *field_name;
+ switch (_geo_qual) {
+ case GEO_QUAL_ASN:
+ field_name = "autonomous_system";
+ break;
+ default:
+ TSDebug(PLUGIN_NAME, "Unsupported field %d", _geo_qual);
+ return ret;
+ break;
+ }
+
+ MMDB_entry_data_s entry_data;
+
+ status = MMDB_get_value(&result.entry, &entry_data, field_name, NULL);
+ if (MMDB_SUCCESS != status) {
+ TSDebug(PLUGIN_NAME, "ERROR on get value asn value: %s", MMDB_strerror(status));
+ return ret;
+ }
+ ret = entry_data.uint32;
+
+ if (NULL != entry_data_list) {
+ MMDB_free_entry_data_list(entry_data_list);
+ }
+
+ return ret;
+}
diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc
index 26f9a4f..961d17a 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -23,6 +23,7 @@
#include "operators.h"
#include "conditions.h"
+#include "conditions_geo.h"
///////////////////////////////////////////////////////////////////////////////
// "Factory" functions, processing the parsed lines
@@ -130,7 +131,13 @@
} else if (c_name == "NOW") {
c = new ConditionNow();
} else if (c_name == "GEO") {
+#if TS_USE_HRW_GEOIP
+ c = new GeoIPConditionGeo();
+#elif TS_USE_HRW_MAXMINDDB
+ c = new MMConditionGeo();
+#else
c = new ConditionGeo();
+#endif
} else if (c_name == "ID") {
c = new ConditionId();
} else if (c_name == "CIDR") {
diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc
index 85e0fee..55d8f0f 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -15,9 +15,12 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
+
#include <fstream>
+#include <mutex>
#include <string>
#include <stdexcept>
+#include <getopt.h>
#include "ts/ts.h"
#include "ts/remap.h"
@@ -25,41 +28,24 @@
#include "parser.h"
#include "ruleset.h"
#include "resources.h"
+#include "conditions.h"
+#include "conditions_geo.h"
// Debugs
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 std::once_flag initGeoLibs;
static void
-initGeoIP()
+initGeoLib(const std::string &dbPath)
{
- GeoIPDBTypes dbs[] = {GEOIP_COUNTRY_EDITION, GEOIP_COUNTRY_EDITION_V6, GEOIP_ASNUM_EDITION, GEOIP_ASNUM_EDITION_V6};
-
- for (auto &db : dbs) {
- if (!gGeoIP[db] && GeoIP_db_avail(db)) {
- // GEOIP_STANDARD seems to break threaded apps...
- gGeoIP[db] = GeoIP_open_type(db, GEOIP_MMAP_CACHE);
-
- char *db_info = GeoIP_database_info(gGeoIP[db]);
- TSDebug(PLUGIN_NAME, "initialized GeoIP-DB[%d] %s", db, db_info);
- free(db_info);
- }
- }
-}
-
-#else
-
-static void
-initGeoIP()
-{
-}
+#if TS_USE_HRW_GEOIP
+ GeoIPConditionGeo::initLibrary(dbPath);
+#elif TS_USE_HRW_MAXMINDDB
+ MMConditionGeo::initLibrary(dbPath);
#endif
+}
// Forward declaration for the main continuation.
static int cont_rewrite_headers(TSCont, TSEvent, void *);
@@ -315,6 +301,8 @@
return 0;
}
+static const struct option longopt[] = {{"geo-db-path", required_argument, NULL, 'm'}, {NULL, no_argument, NULL, '\0'}};
+
///////////////////////////////////////////////////////////////////////////////
// Initialize the InkAPI plugin for the global hooks we support.
//
@@ -329,6 +317,21 @@
if (TS_SUCCESS != TSPluginRegister(&info)) {
TSError("[%s] plugin registration failed", PLUGIN_NAME);
+ return;
+ }
+
+ std::string geoDBpath;
+ while (true) {
+ int opt = getopt_long(argc, (char *const *)argv, "m:", longopt, NULL);
+
+ switch (opt) {
+ case 'm': {
+ geoDBpath = optarg;
+ } break;
+ }
+ if (opt == -1) {
+ break;
+ }
}
// Parse the global config file(s). All rules are just appended
@@ -336,9 +339,9 @@
RulesConfig *conf = new RulesConfig;
bool got_config = false;
- initGeoIP();
+ std::call_once(initGeoLibs, [&geoDBpath]() { initGeoLib(geoDBpath); });
- for (int i = 1; i < argc; ++i) {
+ for (int i = optind; i < argc; ++i) {
// Parse the config file(s). Note that multiple config files are
// just appended to the configurations.
TSDebug(PLUGIN_NAME, "Loading global configuration file %s", argv[i]);
@@ -389,7 +392,6 @@
return TS_ERROR;
}
- initGeoIP();
TSDebug(PLUGIN_NAME, "Remap plugin is successfully initialized");
return TS_SUCCESS;
@@ -405,9 +407,30 @@
return TS_ERROR;
}
+ // argv contains the "to" and "from" URLs. Skip the first so that the
+ // second one poses as the program name.
+ --argc;
+ ++argv;
+
+ std::string geoDBPath;
+ while (true) {
+ int opt = getopt_long(argc, (char *const *)argv, "m:", longopt, NULL);
+
+ switch (opt) {
+ case 'm': {
+ geoDBPath = optarg;
+ } break;
+ }
+ if (opt == -1) {
+ break;
+ }
+ }
+
+ std::call_once(initGeoLibs, [&geoDBPath]() { initGeoLib(geoDBPath); });
+
RulesConfig *conf = new RulesConfig;
- for (int i = 2; i < argc; ++i) {
+ for (int i = optind; i < argc; ++i) {
TSDebug(PLUGIN_NAME, "Loading remap configuration file %s", argv[i]);
if (!conf->parse_config(argv[i], TS_REMAP_PSEUDO_HOOK)) {
TSError("[%s] Unable to create remap instance", PLUGIN_NAME);
diff --git a/plugins/header_rewrite/resources.h b/plugins/header_rewrite/resources.h
index ee5e0f0..c3ddfa0 100644
--- a/plugins/header_rewrite/resources.h
+++ b/plugins/header_rewrite/resources.h
@@ -28,11 +28,6 @@
#include "lulu.h"
-#if HAVE_GEOIP_H
-#include <GeoIP.h>
-extern GeoIP *gGeoIP[NUM_DB_TYPES];
-#endif
-
enum ResourceIDs {
RSRC_NONE = 0,
RSRC_SERVER_RESPONSE_HEADERS = 1,
diff --git a/plugins/healthchecks/Makefile.inc b/plugins/healthchecks/Makefile.inc
index 2121b72..f9c027c 100644
--- a/plugins/healthchecks/Makefile.inc
+++ b/plugins/healthchecks/Makefile.inc
@@ -14,9 +14,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-if BUILD_HEALTHCHECK_PLUGIN
-
pkglib_LTLIBRARIES += healthchecks/healthchecks.la
healthchecks_healthchecks_la_SOURCES = healthchecks/healthchecks.c
-
-endif