aboutsummaryrefslogtreecommitdiff
path: root/src/vmod_geoip.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vmod_geoip.c')
-rw-r--r--src/vmod_geoip.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/vmod_geoip.c b/src/vmod_geoip.c
new file mode 100644
index 0000000..6ad23e2
--- /dev/null
+++ b/src/vmod_geoip.c
@@ -0,0 +1,260 @@
+#include <config.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "vcl.h"
+#include "vrt.h"
+#include "vcc_if.h"
+#include "bin/varnishd/cache/cache.h"
+#include <maxminddb.h>
+#include <assert.h>
+
+#define DEFAULT_DIR "/usr/share/GeoIP"
+#define DEFAULT_FILE "GeoLite2-City.mmdb"
+#define DEFAULT_DATABASE DEFAULT_DIR "/" DEFAULT_FILE
+
+struct geoip_config {
+ char *database;
+};
+
+int
+geoip_event(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
+{
+ if (e == VCL_EVENT_LOAD) {
+ struct geoip_config *conf = calloc(1, sizeof(*conf));
+ AN(conf);
+ priv->priv = conf;
+ }
+ return 0;
+}
+
+void
+vmod_init(VRT_CTX, struct vmod_priv *priv, const char *dir)
+{
+ struct geoip_config *conf = priv->priv;
+ struct stat st;
+ char *name;
+
+ if (stat (dir, &st)) {
+ fprintf (stderr, "geoip.init: can't stat \"%s\": %s\n",
+ dir, strerror (errno));
+ abort ();
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ size_t dlen = strlen (dir);
+ size_t len;
+
+ if (dir[dlen-1] == '/')
+ dlen--;
+ name = malloc (dlen + 1 + sizeof (DEFAULT_FILE));
+ AN(name);
+ memcpy (name, dir, dlen);
+ name[dlen++] = '/';
+ strcpy (name + dlen, DEFAULT_FILE);
+ } else if (S_ISREG (st.st_mode)) {
+ name = strdup (dir);
+ AN(name);
+ } else {
+ fprintf (stderr, "geoip.init: \"%s\": bad file type\n", dir);
+ abort ();
+ }
+
+ free(conf->database);
+ conf->database = name;
+}
+
+static int
+open_geoip_database(struct geoip_config *conf, MMDB_s *dbptr)
+{
+ char *database = conf->database ? conf->database : DEFAULT_DATABASE;
+ int rc;
+
+ rc = MMDB_open(database, MMDB_MODE_MMAP, dbptr);
+ if (rc != MMDB_SUCCESS) {
+ fprintf(stderr, "can't open database \"%s\": %s\n",
+ database, MMDB_strerror(rc));
+ return -1;
+ }
+ return 0;
+}
+
+static char *
+conv_utf_string (struct ws *ws, MMDB_entry_data_s *dptr)
+{
+ char *retval = WS_Alloc(ws, dptr->data_size + 1);
+ AN(retval);
+ memcpy(retval, dptr->utf8_string, dptr->data_size);
+ retval[dptr->data_size] = 0;
+ return retval;
+}
+
+static char *(*entry_conv[]) (struct ws *, MMDB_entry_data_s *) = {
+ [MMDB_DATA_TYPE_UTF8_STRING] = conv_utf_string
+};
+
+static char *
+lookup_geoip_database(struct ws *ws,
+ MMDB_s *dbfile, VCL_STRING ipstr,
+ char const *pathstr, char **path)
+{
+ MMDB_lookup_result_s result;
+ int gai_error, mmdb_error;
+ int rc;
+ MMDB_entry_data_s entry_data;
+ char *retval;
+
+ result = MMDB_lookup_string(dbfile, ipstr, &gai_error, &mmdb_error);
+ if (gai_error) {
+ fprintf(stderr, "%s %s: GAI %s\n",
+ pathstr, ipstr, gai_strerror(gai_error));
+ return NULL;
+ }
+
+ if (mmdb_error != MMDB_SUCCESS) {
+ fprintf(stderr, "%s %s: MMDB %s\n",
+ pathstr, ipstr, MMDB_strerror(mmdb_error));
+ return NULL;
+ }
+
+ if (!result.found_entry)
+ return NULL;
+
+ rc = MMDB_aget_value(&result.entry, &entry_data, (const char * const* const) path);
+ if (rc != MMDB_SUCCESS) {
+ fprintf(stderr, "%s %s: MMDB_aget_value %s\n",
+ pathstr, ipstr, MMDB_strerror(rc));
+ return NULL;
+ }
+
+ if (!entry_data.has_data)
+ return NULL;
+
+ if (entry_data.type >= 0
+ && entry_data.type <= sizeof (entry_conv) / sizeof (entry_conv[0])
+ && entry_conv[entry_data.type]) {
+ retval = entry_conv[entry_data.type] (ws, &entry_data);
+ } else
+ retval = NULL;
+
+ return retval;
+}
+
+static char **
+split(char const *path)
+{
+ int c = 0;
+ int i, j;
+ char *base;
+ char **retv;
+ char *str;
+
+ c = 1;
+ for (i = 0; path[i]; i++)
+ if (path[i] == '.')
+ c++;
+ base = malloc(i + 1 + (c + 1) * sizeof(char*));
+ AN(base);
+ retv = (char**) base;
+ str = (char*) &retv[c+1];
+ strcpy(str, path);
+
+ i = 0;
+ j = 0;
+ retv[j++] = str;
+ while (str[i]) {
+ if (str[i] == '.') {
+ str[i] = 0;
+ retv[j++] = str + i + 1;
+ }
+ i++;
+ }
+ retv[j] = NULL;
+ return retv;
+}
+
+VCL_STRING
+vmod_get(VRT_CTX, struct vmod_priv *priv, VCL_STRING ipstr, VCL_STRING path)
+{
+ struct geoip_config *conf = priv->priv;
+ MMDB_s dbfile;
+ char *retval = NULL;
+ char **pathv;
+
+ if (open_geoip_database(conf, &dbfile))
+ return NULL;
+ pathv = split(path);
+ retval = lookup_geoip_database(ctx->ws, &dbfile, ipstr, path, pathv);
+ free(pathv);
+ MMDB_close(&dbfile);
+ return retval;
+}
+
+VCL_STRING
+vmod_country_code(VRT_CTX, struct vmod_priv *priv, VCL_STRING ipstr)
+{
+ struct geoip_config *conf = priv->priv;
+ MMDB_s dbfile;
+ char *retval = NULL;
+ static char *country_code_path[] = {
+ "country",
+ "iso_code",
+ NULL
+ };
+
+ if (open_geoip_database(conf, &dbfile))
+ return NULL;
+ retval = lookup_geoip_database(ctx->ws, &dbfile, ipstr,
+ "country.iso_code", country_code_path);
+ MMDB_close(&dbfile);
+ return retval;
+}
+
+VCL_STRING
+vmod_country_name(VRT_CTX, struct vmod_priv *priv, VCL_STRING ipstr)
+{
+ struct geoip_config *conf = priv->priv;
+ MMDB_s dbfile;
+ char *retval = NULL;
+ static char *country_name_path[] = {
+ "country",
+ "names",
+ "en",
+ NULL
+ };
+
+ if (open_geoip_database(conf, &dbfile))
+ return NULL;
+ retval = lookup_geoip_database(ctx->ws, &dbfile, ipstr,
+ "country.names.en",
+ country_name_path);
+ MMDB_close(&dbfile);
+ return retval;
+}
+
+VCL_STRING
+vmod_city_name(VRT_CTX, struct vmod_priv *priv, VCL_STRING ipstr)
+{
+ struct geoip_config *conf = priv->priv;
+ MMDB_s dbfile;
+ char *retval = NULL;
+ static char *city_name_path[] = {
+ "city",
+ "names",
+ "en",
+ NULL
+ };
+
+ if (open_geoip_database(conf, &dbfile))
+ return NULL;
+ retval = lookup_geoip_database(ctx->ws, &dbfile, ipstr,
+ "city.names.en",
+ city_name_path);
+ MMDB_close(&dbfile);
+ return retval;
+}

Return to:

Send suggestions and report system problems to the System administrator.