You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by ez...@apache.org on 2022/08/15 14:44:50 UTC

[trafficserver] branch master updated: convert to c++ plugin (#8335)

This is an automated email from the ASF dual-hosted git repository.

eze pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 66412f056 convert to c++ plugin (#8335)
66412f056 is described below

commit 66412f05629e805587a50b536d5cf8aaa6138efe
Author: Evan Zelkowitz <ez...@apache.org>
AuthorDate: Mon Aug 15 08:44:45 2022 -0600

    convert to c++ plugin (#8335)
    
    move ip allow setup to use ipmap
    if a config is used link it to remap to initiate plugin reload on config change
---
 doc/admin-guide/plugins/stats_over_http.en.rst     |   7 +
 plugins/stats_over_http/Makefile.inc               |   2 +-
 .../{stats_over_http.c => stats_over_http.cc}      | 376 +++++++--------------
 3 files changed, 131 insertions(+), 254 deletions(-)

diff --git a/doc/admin-guide/plugins/stats_over_http.en.rst b/doc/admin-guide/plugins/stats_over_http.en.rst
index d09a4a0b8..e4981bd37 100644
--- a/doc/admin-guide/plugins/stats_over_http.en.rst
+++ b/doc/admin-guide/plugins/stats_over_http.en.rst
@@ -41,9 +41,12 @@ default URL::
 
 where host and port is the hostname/IP and port number of the server.
 
+
+
 Plugin Options
 ==============
 
+
 .. option:: --integer-counters
 
 This option causes the plugin to emit floating point and integral
@@ -104,3 +107,7 @@ if you wish to have it in CSV format you can do so by passing an ``Accept`` head
 
 In either case the ``Content-Type`` header returned by stats_over_http.so will reflect
 the content that has been returned, either ``text/json`` or ``text/csv``.
+
+.. option:: Accept-encoding: gzip, br
+
+Stats over http also accepts returning data in gzip or br compressed format
diff --git a/plugins/stats_over_http/Makefile.inc b/plugins/stats_over_http/Makefile.inc
index 442e53184..05f870e12 100644
--- a/plugins/stats_over_http/Makefile.inc
+++ b/plugins/stats_over_http/Makefile.inc
@@ -15,7 +15,7 @@
 #  limitations under the License.
 
 pkglib_LTLIBRARIES += stats_over_http/stats_over_http.la
-stats_over_http_stats_over_http_la_SOURCES = stats_over_http/stats_over_http.c
+stats_over_http_stats_over_http_la_SOURCES = stats_over_http/stats_over_http.cc
 
 stats_over_http_stats_over_http_la_LDFLAGS = \
   $(AM_LDFLAGS) $(BROTLIENC_LIB) $(LIBZ)
diff --git a/plugins/stats_over_http/stats_over_http.c b/plugins/stats_over_http/stats_over_http.cc
similarity index 74%
rename from plugins/stats_over_http/stats_over_http.c
rename to plugins/stats_over_http/stats_over_http.cc
index c480e7900..c8ea08bd7 100644
--- a/plugins/stats_over_http/stats_over_http.c
+++ b/plugins/stats_over_http/stats_over_http.cc
@@ -39,14 +39,16 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <zlib.h>
+#include <fstream>
+#include <ts/remap.h>
 
 #include "ink_autoconf.h"
-
 #if HAVE_BROTLI_ENCODE_H
 #include <brotli/encode.h>
 #endif
 
 #include "tscore/ink_defs.h"
+#include "tscore/IpMap.h"
 
 #define PLUGIN_NAME "stats_over_http"
 #define FREE_TMOUT 300000
@@ -55,11 +57,11 @@
 #define SYSTEM_RECORD_TYPE (0x100)
 #define DEFAULT_RECORD_TYPES (SYSTEM_RECORD_TYPE | TS_RECORDTYPE_PROCESS | TS_RECORDTYPE_PLUGIN)
 
-#define DEFAULT_IP "0.0.0.0"
-#define DEFAULT_IP6 "::"
+std::string_view const DEFAULT_IP  = "0.0.0.0/0";
+std::string_view const DEFAULT_IP6 = "::/0";
 
 /* global holding the path used for access to this JSON data */
-#define DEFAULT_URL_PATH "_stats"
+std::string const DEFAULT_URL_PATH = "_stats";
 
 // from mod_deflate:
 // ZLIB's compression algorithm uses a
@@ -68,7 +70,7 @@
 // to be about the best level to use in an HTTP Server.
 
 const int ZLIB_COMPRESSION_LEVEL = 6;
-const char *dictionary           = NULL;
+const char *dictionary           = nullptr;
 
 // zlib stuff, see [deflateInit2] at http://www.zlib.net/manual.html
 static const int ZLIB_MEMLEVEL = 9; // min=1 (optimize for memory),max=9 (optimized for speed)
@@ -87,23 +89,19 @@ const int BROTLI_LGW               = 16;
 static bool integer_counters = false;
 static bool wrap_counters    = false;
 
-typedef struct {
+struct config_t {
   unsigned int recordTypes;
-  char *stats_path;
-  int stats_path_len;
-  char *allowIps;
-  int ipCount;
-  char *allowIps6;
-  int ip6Count;
-} config_t;
-typedef struct {
+  std::string stats_path;
+  IpMap allow_ip_map;
+};
+struct config_holder_t {
   char *config_path;
   volatile time_t last_load;
   config_t *config;
-} config_holder_t;
+};
 
-typedef enum { JSON_OUTPUT, CSV_OUTPUT } output_format;
-typedef enum { NONE, DEFLATE, GZIP, BR } encoding_format;
+enum output_format { JSON_OUTPUT, CSV_OUTPUT };
+enum encoding_format { NONE, DEFLATE, GZIP, BR };
 
 int configReloadRequests = 0;
 int configReloads        = 0;
@@ -115,10 +113,10 @@ static int free_handler(TSCont cont, TSEvent event, void *edata);
 static int config_handler(TSCont cont, TSEvent event, void *edata);
 static config_t *get_config(TSCont cont);
 static config_holder_t *new_config_holder(const char *path);
-static bool is_ip_allowed(const config_t *config, const struct sockaddr *addr);
+static bool is_ipmap_allowed(const config_t *config, const struct sockaddr *addr);
 
 #if HAVE_BROTLI_ENCODE_H
-typedef struct {
+struct b_stream {
   BrotliEncoderState *br;
   uint8_t *next_in;
   size_t avail_in;
@@ -126,10 +124,10 @@ typedef struct {
   size_t avail_out;
   size_t total_in;
   size_t total_out;
-} b_stream;
+};
 #endif
 
-typedef struct stats_state_t {
+struct stats_state {
   TSVConn net_vc;
   TSVIO read_vio;
   TSVIO write_vio;
@@ -146,7 +144,7 @@ typedef struct stats_state_t {
 #if HAVE_BROTLI_ENCODE_H
   b_stream bstrm;
 #endif
-} stats_state;
+};
 
 static char *
 nstr(const char *s)
@@ -160,19 +158,19 @@ nstr(const char *s)
 encoding_format
 init_br(stats_state *my_state)
 {
-  my_state->bstrm.br = NULL;
+  my_state->bstrm.br = nullptr;
 
-  my_state->bstrm.br = BrotliEncoderCreateInstance(NULL, NULL, NULL);
+  my_state->bstrm.br = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
   if (!my_state->bstrm.br) {
     TSDebug(PLUGIN_NAME, "Brotli Encoder Instance Failed");
     return NONE;
   }
   BrotliEncoderSetParameter(my_state->bstrm.br, BROTLI_PARAM_QUALITY, BROTLI_COMPRESSION_LEVEL);
   BrotliEncoderSetParameter(my_state->bstrm.br, BROTLI_PARAM_LGWIN, BROTLI_LGW);
-  my_state->bstrm.next_in   = NULL;
+  my_state->bstrm.next_in   = nullptr;
   my_state->bstrm.avail_in  = 0;
   my_state->bstrm.total_in  = 0;
-  my_state->bstrm.next_out  = NULL;
+  my_state->bstrm.next_out  = nullptr;
   my_state->bstrm.avail_out = 0;
   my_state->bstrm.total_out = 0;
   return BR;
@@ -212,12 +210,12 @@ stats_cleanup(TSCont contp, stats_state *my_state)
 {
   if (my_state->req_buffer) {
     TSIOBufferDestroy(my_state->req_buffer);
-    my_state->req_buffer = NULL;
+    my_state->req_buffer = nullptr;
   }
 
   if (my_state->resp_buffer) {
     TSIOBufferDestroy(my_state->resp_buffer);
-    my_state->resp_buffer = NULL;
+    my_state->resp_buffer = nullptr;
   }
 
   TSVConnClose(my_state->net_vc);
@@ -364,7 +362,7 @@ static void
 json_out_stat(TSRecordType rec_type ATS_UNUSED, void *edata, int registered ATS_UNUSED, const char *name,
               TSRecordDataType data_type, TSRecordData *datum)
 {
-  stats_state *my_state = edata;
+  stats_state *my_state = static_cast<stats_state *>(edata);
 
   switch (data_type) {
   case TS_RECORDDATATYPE_COUNTER:
@@ -389,7 +387,7 @@ static void
 csv_out_stat(TSRecordType rec_type ATS_UNUSED, void *edata, int registered ATS_UNUSED, const char *name, TSRecordDataType data_type,
              TSRecordData *datum)
 {
-  stats_state *my_state = edata;
+  stats_state *my_state = static_cast<stats_state *>(edata);
   switch (data_type) {
   case TS_RECORDDATATYPE_COUNTER:
     APPEND_STAT_CSV_NUMERIC(name, "%" PRIu64, wrap_unsigned_counter(datum->rec_counter));
@@ -538,7 +536,7 @@ stats_process_write(TSCont contp, TSEvent event, stats_state *my_state)
 static int
 stats_dostuff(TSCont contp, TSEvent event, void *edata)
 {
-  stats_state *my_state = TSContDataGet(contp);
+  stats_state *my_state = static_cast<stats_state *>(TSContDataGet(contp));
   if (event == TS_EVENT_NET_ACCEPT) {
     my_state->net_vc = (TSVConn)edata;
     stats_process_accept(contp, my_state);
@@ -553,15 +551,17 @@ stats_dostuff(TSCont contp, TSEvent event, void *edata)
 }
 
 static int
-stats_origin(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata)
+stats_origin(TSCont contp, TSEvent event, void *edata)
 {
   TSCont icontp;
   stats_state *my_state;
   config_t *config;
   TSHttpTxn txnp = (TSHttpTxn)edata;
   TSMBuffer reqp;
-  TSMLoc hdr_loc = NULL, url_loc = NULL, accept_field = NULL, accept_encoding_field = NULL;
+  TSMLoc hdr_loc = nullptr, url_loc = nullptr, accept_field = nullptr, accept_encoding_field = nullptr;
   TSEvent reenable = TS_EVENT_HTTP_CONTINUE;
+  int path_len     = 0;
+  const char *path = nullptr;
 
   TSDebug(PLUGIN_NAME, "in the read stuff");
   config = get_config(contp);
@@ -574,17 +574,19 @@ stats_origin(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata)
     goto cleanup;
   }
 
-  int path_len     = 0;
-  const char *path = TSUrlPathGet(reqp, url_loc, &path_len);
+  path = TSUrlPathGet(reqp, url_loc, &path_len);
   TSDebug(PLUGIN_NAME, "Path: %.*s", path_len, path);
 
-  if (!(path_len != 0 && path_len == config->stats_path_len && !memcmp(path, config->stats_path, config->stats_path_len))) {
+  if (!(path_len != 0 && path_len == int(config->stats_path.length()) &&
+        !memcmp(path, config->stats_path.c_str(), config->stats_path.length()))) {
+    TSDebug(PLUGIN_NAME, "not this plugins path, saw: %.*s, looking for: %s", path_len, path, config->stats_path.c_str());
     goto notforme;
   }
 
-  const struct sockaddr *addr = TSHttpTxnClientAddrGet(txnp);
-  if (!is_ip_allowed(config, addr)) {
+  if (auto addr = TSHttpTxnClientAddrGet(txnp); !is_ipmap_allowed(config, addr)) {
     TSDebug(PLUGIN_NAME, "not right ip");
+    TSHttpTxnStatusSet(txnp, TS_HTTP_STATUS_FORBIDDEN);
+    reenable = TS_EVENT_HTTP_ERROR;
     goto notforme;
   }
 
@@ -618,15 +620,15 @@ stats_origin(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata)
   if (accept_encoding_field != TS_NULL_MLOC) {
     int len         = -1;
     const char *str = TSMimeHdrFieldValueStringGet(reqp, hdr_loc, accept_encoding_field, -1, &len);
-    if (len >= TS_HTTP_LEN_DEFLATE && strstr(str, TS_HTTP_VALUE_DEFLATE) != NULL) {
+    if (len >= TS_HTTP_LEN_DEFLATE && strstr(str, TS_HTTP_VALUE_DEFLATE) != nullptr) {
       TSDebug(PLUGIN_NAME, "Saw deflate in accept encoding");
       my_state->encoding = init_gzip(my_state, DEFLATE_MODE);
-    } else if (len >= TS_HTTP_LEN_GZIP && strstr(str, TS_HTTP_VALUE_GZIP) != NULL) {
+    } else if (len >= TS_HTTP_LEN_GZIP && strstr(str, TS_HTTP_VALUE_GZIP) != nullptr) {
       TSDebug(PLUGIN_NAME, "Saw gzip in accept encoding");
       my_state->encoding = init_gzip(my_state, GZIP_MODE);
     }
 #if HAVE_BROTLI_ENCODE_H
-    else if (len >= TS_HTTP_LEN_BROTLI && strstr(str, TS_HTTP_VALUE_BROTLI) != NULL) {
+    else if (len >= TS_HTTP_LEN_BROTLI && strstr(str, TS_HTTP_VALUE_BROTLI) != nullptr) {
       TSDebug(PLUGIN_NAME, "Saw br in accept encoding");
       my_state->encoding = init_br(my_state);
     }
@@ -666,9 +668,9 @@ TSPluginInit(int argc, const char *argv[])
   TSPluginRegistrationInfo info;
 
   static const char usage[]             = PLUGIN_NAME ".so [--integer-counters] [PATH]";
-  static const struct option longopts[] = {{(char *)("integer-counters"), no_argument, NULL, 'i'},
-                                           {(char *)("wrap-counters"), no_argument, NULL, 'w'},
-                                           {NULL, 0, NULL, 0}};
+  static const struct option longopts[] = {{(char *)("integer-counters"), no_argument, nullptr, 'i'},
+                                           {(char *)("wrap-counters"), no_argument, nullptr, 'w'},
+                                           {nullptr, 0, nullptr, 0}};
   TSCont main_cont, config_cont;
   config_holder_t *config_holder;
 
@@ -682,7 +684,7 @@ TSPluginInit(int argc, const char *argv[])
   }
 
   for (;;) {
-    switch (getopt_long(argc, (char *const *)argv, "iw", longopts, NULL)) {
+    switch (getopt_long(argc, (char *const *)argv, "iw", longopts, nullptr)) {
     case 'i':
       integer_counters = true;
       break;
@@ -700,23 +702,21 @@ init:
   argc -= optind;
   argv += optind;
 
-  config_holder = new_config_holder(argc > 0 ? argv[0] : NULL);
+  config_holder = new_config_holder(argc > 0 ? argv[0] : nullptr);
 
   /* Path was not set during load, so the param was not a config file, we also
     have an argument so it must be the path, set it here.  Otherwise if no argument
     then use the default _stats path */
-  if ((config_holder->config != NULL) && (config_holder->config->stats_path == 0) && (argc > 0) &&
-      (config_holder->config_path == NULL)) {
-    config_holder->config->stats_path     = TSstrdup(argv[0] + ('/' == argv[0][0] ? 1 : 0));
-    config_holder->config->stats_path_len = strlen(config_holder->config->stats_path);
-  } else if ((config_holder->config != NULL) && (config_holder->config->stats_path == 0)) {
-    config_holder->config->stats_path     = nstr(DEFAULT_URL_PATH);
-    config_holder->config->stats_path_len = strlen(config_holder->config->stats_path);
+  if ((config_holder->config != nullptr) && (config_holder->config->stats_path.empty()) && (argc > 0) &&
+      (config_holder->config_path == nullptr)) {
+    config_holder->config->stats_path = argv[0] + ('/' == argv[0][0] ? 1 : 0);
+  } else if ((config_holder->config != nullptr) && (config_holder->config->stats_path.empty())) {
+    config_holder->config->stats_path = DEFAULT_URL_PATH;
   }
 
   /* Create a continuation with a mutex as there is a shared global structure
      containing the headers to add */
-  main_cont = TSContCreate(stats_origin, NULL);
+  main_cont = TSContCreate(stats_origin, nullptr);
   TSContDataSet(main_cont, (void *)config_holder);
   TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, main_cont);
 
@@ -724,231 +724,105 @@ init:
   config_cont = TSContCreate(config_handler, TSMutexCreate());
   TSContDataSet(config_cont, (void *)config_holder);
   TSMgmtUpdateRegister(config_cont, PLUGIN_NAME);
-  TSDebug(PLUGIN_NAME, "stats module registered with path %s", config_holder->config->stats_path);
+  TSDebug(PLUGIN_NAME, "stats module registered with path %s", config_holder->config->stats_path.c_str());
 
 done:
   return;
 }
 
 static bool
-is_ip_match(const char *ip, char *ipmask, char mask)
+is_ipmap_allowed(const config_t *config, const struct sockaddr *addr)
 {
-  unsigned int j, i, k;
-  char cm;
-  // to be able to set mask to 128
-  unsigned int umask = 0xff & mask;
-
-  for (j = 0, i = 0; ((i + 1) * 8) <= umask; i++) {
-    if (ip[i] != ipmask[i]) {
-      return false;
-    }
-    j += 8;
-  }
-  cm = 0;
-  for (k = 0; j < umask; j++, k++) {
-    cm |= 1 << (7 - k);
-  }
-
-  if ((ip[i] & cm) != (ipmask[i] & cm)) {
-    return false;
-  }
-  return true;
-}
-
-static bool
-is_ip_allowed(const config_t *config, const struct sockaddr *addr)
-{
-  char ip_port_text_buffer[INET6_ADDRSTRLEN];
-  int i;
-  char *ipmask;
   if (!addr) {
     return true;
   }
 
-  if (addr->sa_family == AF_INET && config->allowIps) {
-    const struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
-    const char *ip                    = (char *)&addr_in->sin_addr;
-
-    for (i = 0; i < config->ipCount; i++) {
-      ipmask = config->allowIps + (i * (sizeof(struct in_addr) + 1));
-      if (is_ip_match(ip, ipmask, ipmask[4])) {
-        TSDebug(PLUGIN_NAME, "clientip is %s--> ALLOW", inet_ntop(AF_INET, ip, ip_port_text_buffer, INET6_ADDRSTRLEN));
-        return true;
-      }
-    }
-    TSDebug(PLUGIN_NAME, "clientip is %s--> DENY", inet_ntop(AF_INET, ip, ip_port_text_buffer, INET6_ADDRSTRLEN));
-    return false;
-
-  } else if (addr->sa_family == AF_INET6 && config->allowIps6) {
-    const struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
-    const char *ip                      = (char *)&addr_in6->sin6_addr;
-
-    for (i = 0; i < config->ip6Count; i++) {
-      ipmask = config->allowIps6 + (i * (sizeof(struct in6_addr) + 1));
-      if (is_ip_match(ip, ipmask, ipmask[sizeof(struct in6_addr)])) {
-        TSDebug(PLUGIN_NAME, "clientip6 is %s--> ALLOW", inet_ntop(AF_INET6, ip, ip_port_text_buffer, INET6_ADDRSTRLEN));
-        return true;
-      }
-    }
-    TSDebug(PLUGIN_NAME, "clientip6 is %s--> DENY", inet_ntop(AF_INET6, ip, ip_port_text_buffer, INET6_ADDRSTRLEN));
-    return false;
-  }
-  return true;
-}
-
-static void
-parseIps(config_t *config, char *ipStr)
-{
-  char buffer[STR_BUFFER_SIZE];
-  char *p, *tok1, *tok2, *ip;
-  int i, mask;
-  char ip_port_text_buffer[INET_ADDRSTRLEN];
-
-  if (!ipStr) {
-    config->ipCount = 1;
-    ip = config->allowIps = TSmalloc(sizeof(struct in_addr) + 1);
-    inet_pton(AF_INET, DEFAULT_IP, ip);
-    ip[4] = 0;
-    return;
-  }
-
-  strcpy(buffer, ipStr);
-  p = buffer;
-  while (strtok_r(p, ", \n", &p)) {
-    config->ipCount++;
-  }
-  if (!config->ipCount) {
-    return;
+  if (config->allow_ip_map.contains(addr, nullptr)) {
+    return true;
   }
-  config->allowIps = TSmalloc(5 * config->ipCount); // 4 bytes for ip + 1 for bit mask
-  strcpy(buffer, ipStr);
-  p = buffer;
-  i = 0;
-  while ((tok1 = strtok_r(p, ", \n", &p))) {
-    TSDebug(PLUGIN_NAME, "%d) parsing: %s", i + 1, tok1);
-    tok2 = strtok_r(tok1, "/", &tok1);
-    ip   = config->allowIps + ((sizeof(struct in_addr) + 1) * i);
-    if (!inet_pton(AF_INET, tok2, ip)) {
-      TSDebug(PLUGIN_NAME, "%d) skipping: %s", i + 1, tok1);
-      continue;
-    }
 
-    if (tok1 != NULL) {
-      tok2 = strtok_r(tok1, "/", &tok1);
-    }
-    if (!tok2) {
-      mask = 32;
-    } else {
-      mask = atoi(tok2);
-    }
-    ip[4] = mask;
-    TSDebug(PLUGIN_NAME, "%d) adding netmask: %s/%d", i + 1, inet_ntop(AF_INET, ip, ip_port_text_buffer, INET_ADDRSTRLEN), ip[4]);
-    i++;
-  }
+  return false;
 }
 static void
-parseIps6(config_t *config, char *ipStr)
+parseIpMap(config_t *config, const char *ipStr)
 {
-  char buffer[STR_BUFFER_SIZE];
-  char *p, *tok1, *tok2, *ip;
-  int i, mask;
-  char ip_port_text_buffer[INET6_ADDRSTRLEN];
-
-  if (!ipStr) {
-    config->ip6Count = 1;
-    ip = config->allowIps6 = TSmalloc(sizeof(struct in6_addr) + 1);
-    inet_pton(AF_INET6, DEFAULT_IP6, ip);
-    ip[sizeof(struct in6_addr)] = 0;
-    return;
-  }
+  IpAddr min, max;
+  ts::TextView ipstring("");
 
-  strcpy(buffer, ipStr);
-  p = buffer;
-  while (strtok_r(p, ", \n", &p)) {
-    config->ip6Count++;
+  if (ipStr != 0) {
+    ipstring.assign(ipStr, strlen(ipStr));
   }
-  if (!config->ip6Count) {
+
+  // sent null ipstring, fill with default open IPs
+  if (ipStr == nullptr) {
+    ats_ip_range_parse(DEFAULT_IP6, min, max);
+    config->allow_ip_map.fill(min, max, nullptr);
+    ats_ip_range_parse(DEFAULT_IP, min, max);
+    config->allow_ip_map.fill(min, max, nullptr);
+    TSDebug(PLUGIN_NAME, "Empty allow settings, setting all IPs in allow list");
     return;
   }
 
-  config->allowIps6 = TSmalloc((sizeof(struct in6_addr) + 1) * config->ip6Count); // 16 bytes for ip + 1 for bit mask
-  strcpy(buffer, ipStr);
-  p = buffer;
-  i = 0;
-  while ((tok1 = strtok_r(p, ", \n", &p))) {
-    TSDebug(PLUGIN_NAME, "%d) parsing: %s", i + 1, tok1);
-    tok2 = strtok_r(tok1, "/", &tok1);
-    ip   = config->allowIps6 + ((sizeof(struct in6_addr) + 1) * i);
-    if (!inet_pton(AF_INET6, tok2, ip)) {
-      TSDebug(PLUGIN_NAME, "%d) skipping: %s", i + 1, tok1);
-      continue;
-    }
-
-    if (tok1 != NULL) {
-      tok2 = strtok_r(tok1, "/", &tok1);
-    }
-
-    if (!tok2) {
-      mask = 128;
-    } else {
-      mask = atoi(tok2);
-    }
-    ip[sizeof(struct in6_addr)] = mask;
-    TSDebug(PLUGIN_NAME, "%d) adding netmask: %s/%d", i + 1, inet_ntop(AF_INET6, ip, ip_port_text_buffer, INET6_ADDRSTRLEN),
-            ip[sizeof(struct in6_addr)]);
-    i++;
+  while (ipstring) {
+    ts::TextView token{ipstring.take_prefix_at(',')};
+    ats_ip_range_parse(std::string_view{token}, min, max);
+    config->allow_ip_map.fill(min, max, nullptr);
+    TSDebug(PLUGIN_NAME, "Added %.*s to allow ip list", int(token.length()), token.data());
   }
 }
 
 static config_t *
-new_config(TSFile fh)
+new_config(std::fstream &fh)
 {
-  char buffer[STR_BUFFER_SIZE];
-  config_t *config       = NULL;
-  config                 = (config_t *)TSmalloc(sizeof(config_t));
-  config->stats_path     = 0;
-  config->stats_path_len = 0;
-  config->allowIps       = 0;
-  config->ipCount        = 0;
-  config->allowIps6      = 0;
-  config->ip6Count       = 0;
-  config->recordTypes    = DEFAULT_RECORD_TYPES;
+  config_t *config    = nullptr;
+  config              = new config_t();
+  config->recordTypes = DEFAULT_RECORD_TYPES;
+  config->stats_path  = "";
+  std::string cur_line;
+  IpAddr min, max;
 
   if (!fh) {
     TSDebug(PLUGIN_NAME, "No config file, using defaults");
     return config;
   }
 
-  while (TSfgets(fh, buffer, STR_BUFFER_SIZE - 1)) {
-    if (*buffer == '#') {
+  while (std::getline(fh, cur_line)) {
+    // skip empty lines
+    if (cur_line == "") {
+      continue;
+    }
+    if (cur_line.at(0) == '#') {
       continue; /* # Comments, only at line beginning */
     }
-    char *p = 0;
-    if ((p = strstr(buffer, "path="))) {
+
+    size_t p = 0;
+
+    if ((p = cur_line.find("path=")) != std::string::npos) {
+      TSDebug(PLUGIN_NAME, "parsing path");
       p += strlen("path=");
-      if (p[0] == '/') {
+      if (cur_line.at(p + 1) == '/') {
         p++;
       }
-      config->stats_path     = nstr(strtok_r(p, " \n", &p));
-      config->stats_path_len = strlen(config->stats_path);
-    } else if ((p = strstr(buffer, "record_types="))) {
+      config->stats_path = cur_line.substr(p);
+    } else if ((p = cur_line.find("record_types=")) != std::string::npos) {
+      TSDebug(PLUGIN_NAME, "parsing record types");
       p += strlen("record_types=");
-      config->recordTypes = strtol(strtok_r(p, " \n", &p), NULL, 16);
-    } else if ((p = strstr(buffer, "allow_ip="))) {
+      config->recordTypes = strtol(cur_line.substr(p).c_str(), nullptr, 16);
+    } else if ((p = cur_line.find("allow_ip=")) != std::string::npos) {
       p += strlen("allow_ip=");
-      parseIps(config, p);
-    } else if ((p = strstr(buffer, "allow_ip6="))) {
+      parseIpMap(config, cur_line.substr(p).c_str());
+    } else if ((p = cur_line.find("allow_ip6=")) != std::string::npos) {
       p += strlen("allow_ip6=");
-      parseIps6(config, p);
+      parseIpMap(config, cur_line.substr(p).c_str());
     }
   }
-  if (!config->ipCount) {
-    parseIps(config, NULL);
-  }
-  if (!config->ip6Count) {
-    parseIps6(config, NULL);
+
+  if (config->allow_ip_map.count() == 0) {
+    TSDebug(PLUGIN_NAME, "empty ip map found, setting defaults");
+    parseIpMap(config, nullptr);
   }
-  TSDebug(PLUGIN_NAME, "config path=%s", config->stats_path);
+
+  TSDebug(PLUGIN_NAME, "config path=%s", config->stats_path.c_str());
 
   return config;
 }
@@ -957,9 +831,6 @@ static void
 delete_config(config_t *config)
 {
   TSDebug(PLUGIN_NAME, "Freeing config");
-  TSfree(config->allowIps);
-  TSfree(config->allowIps6);
-  TSfree(config->stats_path);
   TSfree(config);
 }
 
@@ -977,19 +848,19 @@ get_config(TSCont cont)
 static void
 load_config_file(config_holder_t *config_holder)
 {
-  TSFile fh = NULL;
+  std::fstream fh;
   struct stat s;
 
   config_t *newconfig, *oldconfig;
   TSCont free_cont;
 
   configReloadRequests++;
-  lastReloadRequest = time(NULL);
+  lastReloadRequest = time(nullptr);
 
   // check date
-  if ((config_holder->config_path == NULL) || (stat(config_holder->config_path, &s) < 0)) {
+  if ((config_holder->config_path == nullptr) || (stat(config_holder->config_path, &s) < 0)) {
     TSDebug(PLUGIN_NAME, "Could not stat %s", config_holder->config_path);
-    config_holder->config_path = NULL;
+    config_holder->config_path = nullptr;
     if (config_holder->config) {
       return;
     }
@@ -1000,14 +871,14 @@ load_config_file(config_holder_t *config_holder)
     }
   }
 
-  if (config_holder->config_path != NULL) {
+  if (config_holder->config_path != nullptr) {
     TSDebug(PLUGIN_NAME, "Opening config file: %s", config_holder->config_path);
-    fh = TSfopen(config_holder->config_path, "r");
+    fh.open(config_holder->config_path, std::ios::in);
   }
 
-  if (!fh && config_holder->config_path != NULL) {
+  if (!fh.is_open() && config_holder->config_path != nullptr) {
     TSError("[%s] Unable to open config: %s. Will use the param as the path, or %s if null\n", PLUGIN_NAME,
-            config_holder->config_path, DEFAULT_URL_PATH);
+            config_holder->config_path, DEFAULT_URL_PATH.c_str());
     if (config_holder->config) {
       return;
     }
@@ -1029,7 +900,7 @@ load_config_file(config_holder_t *config_holder)
     }
   }
   if (fh) {
-    TSfclose(fh);
+    fh.close();
   }
   return;
 }
@@ -1037,7 +908,7 @@ load_config_file(config_holder_t *config_holder)
 static config_holder_t *
 new_config_holder(const char *path)
 {
-  config_holder_t *config_holder = TSmalloc(sizeof(config_holder_t));
+  config_holder_t *config_holder = static_cast<config_holder_t *>(TSmalloc(sizeof(config_holder_t)));
   config_holder->config_path     = 0;
   config_holder->config          = 0;
   config_holder->last_load       = 0;
@@ -1045,7 +916,7 @@ new_config_holder(const char *path)
   if (path) {
     config_holder->config_path = nstr(path);
   } else {
-    config_holder->config_path = NULL;
+    config_holder->config_path = nullptr;
   }
   load_config_file(config_holder);
   return config_holder;
@@ -1070,9 +941,8 @@ config_handler(TSCont cont, TSEvent event, void *edata)
 
   /* We received a reload, check if the path value was removed since it was not set after load.
      If unset, then we'll use the default */
-  if (config_holder->config->stats_path == 0) {
-    config_holder->config->stats_path     = nstr(DEFAULT_URL_PATH);
-    config_holder->config->stats_path_len = strlen(config_holder->config->stats_path);
+  if (config_holder->config->stats_path == "") {
+    config_holder->config->stats_path = DEFAULT_URL_PATH;
   }
   return 0;
 }