package geo import ( "bytes" "context" "encoding/binary" "errors" "os" "path/filepath" "testing" "time" ) // pathologicalMMDB builds a structurally valid IPv4 database of 32 nodes // whose two children are both the next node: 2^32 network paths through 32 // nodes (code review R8-1). func pathologicalMMDB(t *testing.T) string { t.Helper() const nodes = 32 var buf bytes.Buffer rec := func(v uint32) { buf.Write([]byte{byte(v >> 16), byte(v >> 8), byte(v)}) } // 24-bit records for i := 0; i < nodes; i++ { next := uint32(i + 1) if i == nodes-1 { next = nodes + 16 // data section offset 0 } rec(next) rec(next) } buf.Write(make([]byte, 16)) // data section separator buf.WriteByte(0xE0) // the one record: an empty map str := func(s string) { buf.WriteByte(0x40 | byte(len(s))); buf.WriteString(s) } u16 := func(v uint16) { buf.WriteByte(0xA2); binary.Write(&buf, binary.BigEndian, v) } u32 := func(v uint32) { buf.WriteByte(0xC4); binary.Write(&buf, binary.BigEndian, v) } buf.WriteString("\xAB\xCD\xEFMaxMind.com") buf.WriteByte(0xE0 | 9) // metadata map, 9 keys str("node_count") u32(nodes) str("record_size") u16(24) str("ip_version") u16(4) str("database_type") str("Pathological") str("languages") buf.Write([]byte{0x00, 0x04}) // array of 0 str("binary_format_major_version") u16(2) str("binary_format_minor_version") u16(0) str("build_epoch") buf.Write([]byte{0x08, 0x02}) // uint64, 8 bytes binary.Write(&buf, binary.BigEndian, uint64(time.Now().Unix())) str("description") buf.WriteByte(0xE0) // empty map p := filepath.Join(t.TempDir(), "dag.mmdb") if err := os.WriteFile(p, buf.Bytes(), 0o644); err != nil { t.Fatal(err) } return p } func TestOpenMMDBRefusesAPathologicalTreeInTime(t *testing.T) { p := pathologicalMMDB(t) start := time.Now() _, err := OpenMMDBVerified(context.Background(), p) if !errors.Is(err, ErrSearchTreeTooLarge) { t.Fatalf("err = %v, want ErrSearchTreeTooLarge", err) } if d := time.Since(start); d > 5*time.Second { t.Fatalf("took %s", d) } // The real fixtures still open. for _, f := range []string{"GeoIP2-City-Test.mmdb", "GeoIP2-Country-Test.mmdb", "MaxMind-DB-test-ipv4-24.mmdb"} { m, err := OpenMMDBVerified(context.Background(), "testdata/"+f) if err != nil { t.Fatalf("%s: %v", f, err) } m.Close() } } // TestRealDatabaseOpensVerified runs only with GEOIP_REAL_MMDB set to a real // database (DB-IP Lite, GeoLite2): the bounded walk must accept it. func TestRealDatabaseOpensVerified(t *testing.T) { p := os.Getenv("GEOIP_REAL_MMDB") if p == "" { t.Skip("set GEOIP_REAL_MMDB to a real .mmdb") } start := time.Now() m, err := OpenMMDBVerified(context.Background(), p) if err != nil { t.Fatal(err) } m.Close() t.Logf("%s verified in %s", m.Source(), time.Since(start)) }