aboutsummaryrefslogtreecommitdiff
path: root/plugin/geoip/geoip.go
diff options
context:
space:
mode:
authorGravatar Sven Nebel <nebel.sven@gmail.com> 2021-07-14 08:25:30 +0100
committerGravatar GitHub <noreply@github.com> 2021-07-14 09:25:30 +0200
commit21f1207afee6915c14e1109834e3fc0dfed9f420 (patch)
tree19423b6bf9a4ed6b4b43576eb31547441bab07a3 /plugin/geoip/geoip.go
parent936b483a3afdb532180dd6da6fa3c686c5ca9ee9 (diff)
downloadcoredns-21f1207afee6915c14e1109834e3fc0dfed9f420.tar.gz
coredns-21f1207afee6915c14e1109834e3fc0dfed9f420.tar.zst
coredns-21f1207afee6915c14e1109834e3fc0dfed9f420.zip
Create geoip plugin (#4688)
* Create geoip plugin Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Update plugin/geoip/README.md Co-authored-by: Miek Gieben <miek@miek.nl> Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Update plugin/geoip/README.md Co-authored-by: Miek Gieben <miek@miek.nl> Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Update plugin/geoip/README.md Co-authored-by: Miek Gieben <miek@miek.nl> Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Move DBFILE bullet below example Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Update plugin/geoip/README.md Co-authored-by: Miek Gieben <miek@miek.nl> Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Remove plugin name test case Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Remove languages option Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Update free database link Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Remove last language bits Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Use 127.0.0.1 as probing IP Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Update plugin/geoip/geoip.go Co-authored-by: Miek Gieben <miek@miek.nl> Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Update plugin/geoip/geoip.go Co-authored-by: Miek Gieben <miek@miek.nl> Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Use relative path for fixtures dir Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Set names with default string zero value Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Remove unused db types Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Remove non city databases in testdata Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Remove create databases main Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Fix metadata label format test case Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Fix import path block Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * go fmt after changes Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Tidy up go.mod and go.sum Signed-off-by: Sven Nebel <nebel.sven@gmail.com> * Add plugin to CODEOWNERS Signed-off-by: Sven Nebel <nebel.sven@gmail.com> Co-authored-by: Miek Gieben <miek@miek.nl>
Diffstat (limited to 'plugin/geoip/geoip.go')
-rw-r--r--plugin/geoip/geoip.go95
1 files changed, 95 insertions, 0 deletions
diff --git a/plugin/geoip/geoip.go b/plugin/geoip/geoip.go
new file mode 100644
index 000000000..674157716
--- /dev/null
+++ b/plugin/geoip/geoip.go
@@ -0,0 +1,95 @@
+// Package geoip implements a max mind database plugin.
+package geoip
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "path/filepath"
+
+ "github.com/coredns/coredns/plugin"
+ clog "github.com/coredns/coredns/plugin/pkg/log"
+ "github.com/coredns/coredns/request"
+
+ "github.com/miekg/dns"
+ "github.com/oschwald/geoip2-golang"
+)
+
+var log = clog.NewWithPlugin(pluginName)
+
+// GeoIP is a plugin that add geo location data to the request context by looking up a maxmind
+// geoIP2 database, and which data can be later consumed by other middlewares.
+type GeoIP struct {
+ Next plugin.Handler
+ db db
+}
+
+type db struct {
+ *geoip2.Reader
+ // provides defines the schemas that can be obtained by querying this database, by using
+ // bitwise operations.
+ provides int
+}
+
+const (
+ city = 1 << iota
+)
+
+var probingIP = net.ParseIP("127.0.0.1")
+
+func newGeoIP(dbPath string) (*GeoIP, error) {
+ reader, err := geoip2.Open(dbPath)
+ if err != nil {
+ return nil, fmt.Errorf("failed to open database file: %v", err)
+ }
+ db := db{Reader: reader}
+ schemas := []struct {
+ provides int
+ name string
+ validate func() error
+ }{
+ {name: "city", provides: city, validate: func() error { _, err := reader.City(probingIP); return err }},
+ }
+ // Query the database to figure out the database type.
+ for _, schema := range schemas {
+ if err := schema.validate(); err != nil {
+ // If we get an InvalidMethodError then we know this database does not provide that schema.
+ if _, ok := err.(geoip2.InvalidMethodError); !ok {
+ return nil, fmt.Errorf("unexpected failure looking up database %q schema %q: %v", filepath.Base(dbPath), schema.name, err)
+ }
+ } else {
+ db.provides = db.provides | schema.provides
+ }
+ }
+
+ if db.provides&city == 0 {
+ return nil, fmt.Errorf("database does not provide city schema")
+ }
+
+ return &GeoIP{db: db}, nil
+}
+
+// ServeDNS implements the plugin.Handler interface.
+func (g GeoIP) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
+ return plugin.NextOrFailure(pluginName, g.Next, ctx, w, r)
+}
+
+// Metadata implements the metadata.Provider Interface in the metadata plugin, and is used to store
+// the data associated with the source IP of every request.
+func (g GeoIP) Metadata(ctx context.Context, state request.Request) context.Context {
+ srcIP := net.ParseIP(state.IP())
+
+ switch {
+ case g.db.provides&city == city:
+ data, err := g.db.City(srcIP)
+ if err != nil {
+ log.Debugf("Setting up metadata failed due to database lookup error: %v", err)
+ return ctx
+ }
+ g.setCityMetadata(ctx, data)
+ }
+ return ctx
+}
+
+// Name implements the Handler interface.
+func (g GeoIP) Name() string { return pluginName }