mirror of
https://github.com/coredns/coredns.git
synced 2026-01-16 05:41:18 -05:00
plugin/geoip: Add ASN schema support (#7730)
Adds the ability to query ASN .mmdb databases, in addition to the existing City db functionality. Signed-off-by: Eric Case <eric.case@gmail.com>
This commit is contained in:
@@ -17,39 +17,47 @@ func TestMetadata(t *testing.T) {
|
||||
tests := []struct {
|
||||
label string
|
||||
expectedValue string
|
||||
dbPath string
|
||||
remoteIP string
|
||||
}{
|
||||
{"geoip/city/name", "Cambridge"},
|
||||
|
||||
{"geoip/country/code", "GB"},
|
||||
{"geoip/country/name", "United Kingdom"},
|
||||
// City database tests
|
||||
{"geoip/city/name", "Cambridge", cityDBPath, "81.2.69.142"},
|
||||
{"geoip/country/code", "GB", cityDBPath, "81.2.69.142"},
|
||||
{"geoip/country/name", "United Kingdom", cityDBPath, "81.2.69.142"},
|
||||
// is_in_european_union is set to true only to work around bool zero value, and test is really being set.
|
||||
{"geoip/country/is_in_european_union", "true"},
|
||||
{"geoip/country/is_in_european_union", "true", cityDBPath, "81.2.69.142"},
|
||||
{"geoip/continent/code", "EU", cityDBPath, "81.2.69.142"},
|
||||
{"geoip/continent/name", "Europe", cityDBPath, "81.2.69.142"},
|
||||
{"geoip/latitude", "52.2242", cityDBPath, "81.2.69.142"},
|
||||
{"geoip/longitude", "0.1315", cityDBPath, "81.2.69.142"},
|
||||
{"geoip/timezone", "Europe/London", cityDBPath, "81.2.69.142"},
|
||||
{"geoip/postalcode", "CB4", cityDBPath, "81.2.69.142"},
|
||||
|
||||
{"geoip/continent/code", "EU"},
|
||||
{"geoip/continent/name", "Europe"},
|
||||
// ASN database tests
|
||||
{"geoip/asn/number", "12345", asnDBPath, "81.2.69.142"},
|
||||
{"geoip/asn/org", "Test ASN Organization", asnDBPath, "81.2.69.142"},
|
||||
|
||||
{"geoip/latitude", "52.2242"},
|
||||
{"geoip/longitude", "0.1315"},
|
||||
{"geoip/timezone", "Europe/London"},
|
||||
{"geoip/postalcode", "CB4"},
|
||||
// ASN "Not routed" edge case tests (ASN=0)
|
||||
// Test data from iptoasn.com where some IP ranges have no assigned ASN.
|
||||
{"geoip/asn/number", "0", asnDBPath, "10.0.0.1"},
|
||||
{"geoip/asn/org", "Not routed", asnDBPath, "10.0.0.1"},
|
||||
}
|
||||
|
||||
knownIPAddr := "81.2.69.142" // This IP should be part of the CDIR address range used to create the database fixtures.
|
||||
for _, tc := range tests {
|
||||
t.Run(fmt.Sprintf("%s/%s", tc.label, "direct"), func(t *testing.T) {
|
||||
geoIP, err := newGeoIP(cityDBPath, false)
|
||||
geoIP, err := newGeoIP(tc.dbPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create geoIP plugin: %v", err)
|
||||
}
|
||||
state := request.Request{
|
||||
Req: new(dns.Msg),
|
||||
W: &test.ResponseWriter{RemoteIP: knownIPAddr},
|
||||
W: &test.ResponseWriter{RemoteIP: tc.remoteIP},
|
||||
}
|
||||
testMetadata(t, state, geoIP, tc.label, tc.expectedValue)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("%s/%s", tc.label, "subnet"), func(t *testing.T) {
|
||||
geoIP, err := newGeoIP(cityDBPath, true)
|
||||
geoIP, err := newGeoIP(tc.dbPath, true)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create geoIP plugin: %v", err)
|
||||
}
|
||||
@@ -59,7 +67,7 @@ func TestMetadata(t *testing.T) {
|
||||
}
|
||||
state.Req.SetEdns0(4096, false)
|
||||
if o := state.Req.IsEdns0(); o != nil {
|
||||
addr := net.ParseIP(knownIPAddr)
|
||||
addr := net.ParseIP(tc.remoteIP)
|
||||
o.Option = append(o.Option, (&dns.EDNS0_SUBNET{
|
||||
SourceNetmask: 32,
|
||||
Address: addr,
|
||||
@@ -70,6 +78,47 @@ func TestMetadata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetadataUnknownIP(t *testing.T) {
|
||||
// Test that looking up an IP not explicitly in the database doesn't crash.
|
||||
// With IncludeReservedNetworks enabled in the test fixture, the geoip2 library
|
||||
// returns zero-initialized data rather than an error, so metadata is set with
|
||||
// zero values (ASN="0", org="").
|
||||
unknownIPAddr := "203.0.113.1" // TEST-NET-3, not explicitly in our fixture.
|
||||
|
||||
geoIP, err := newGeoIP(asnDBPath, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create geoIP plugin: %v", err)
|
||||
}
|
||||
|
||||
state := request.Request{
|
||||
Req: new(dns.Msg),
|
||||
W: &test.ResponseWriter{RemoteIP: unknownIPAddr},
|
||||
}
|
||||
|
||||
ctx := metadata.ContextWithMetadata(context.Background())
|
||||
geoIP.Metadata(ctx, state)
|
||||
|
||||
// For IPs not in the database, geoip2 returns zero values rather than errors.
|
||||
// Metadata is set with these zero values.
|
||||
fn := metadata.ValueFunc(ctx, "geoip/asn/number")
|
||||
if fn == nil {
|
||||
t.Errorf("expected metadata to be set for unknown IP")
|
||||
return
|
||||
}
|
||||
if fn() != "0" {
|
||||
t.Errorf("expected geoip/asn/number to be \"0\" for unknown IP, got %q", fn())
|
||||
}
|
||||
|
||||
fn = metadata.ValueFunc(ctx, "geoip/asn/org")
|
||||
if fn == nil {
|
||||
t.Errorf("expected metadata to be set for unknown IP")
|
||||
return
|
||||
}
|
||||
if fn() != "" {
|
||||
t.Errorf("expected geoip/asn/org to be empty for unknown IP, got %q", fn())
|
||||
}
|
||||
}
|
||||
|
||||
func testMetadata(t *testing.T, state request.Request, geoIP *GeoIP, label, expectedValue string) {
|
||||
t.Helper()
|
||||
ctx := metadata.ContextWithMetadata(context.Background())
|
||||
|
||||
Reference in New Issue
Block a user