├── .gitattributes ├── .gitconfig ├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── go.yml │ ├── golangci-lint.yml │ └── zizmor.yml ├── .gitignore ├── .golangci.yml ├── .tidyallrc ├── LICENSE-APACHE ├── LICENSE-MIT ├── MaxMind-DB-spec.md ├── README.md ├── bad-data ├── README.md ├── libmaxminddb │ └── libmaxminddb-offset-integer-overflow.mmdb ├── maxminddb-golang │ ├── cyclic-data-structure.mmdb │ ├── invalid-bytes-length.mmdb │ ├── invalid-data-record-offset.mmdb │ ├── invalid-map-key-length.mmdb │ ├── invalid-string-length.mmdb │ ├── metadata-is-an-uint128.mmdb │ └── unexpected-bytes.mmdb └── maxminddb-python │ └── bad-unicode-in-map-key.mmdb ├── cmd └── write-test-data │ └── main.go ├── go.mod ├── go.sum ├── pkg └── writer │ ├── decoder.go │ ├── geoip2.go │ ├── ip.go │ ├── maxmind.go │ ├── nestedstructures.go │ └── writer.go ├── source-data ├── GeoIP-Anonymous-Plus-Test.json ├── GeoIP2-Anonymous-IP-Test.json ├── GeoIP2-City-Test.json ├── GeoIP2-Connection-Type-Test.json ├── GeoIP2-Country-Test.json ├── GeoIP2-DensityIncome-Test.json ├── GeoIP2-Domain-Test.json ├── GeoIP2-Enterprise-Test.json ├── GeoIP2-IP-Risk-Test.json ├── GeoIP2-ISP-Test.json ├── GeoIP2-Precision-Enterprise-Sandbox-Test.json ├── GeoIP2-Precision-Enterprise-Test.json ├── GeoIP2-Static-IP-Score-Test.json ├── GeoIP2-User-Count-Test.json ├── GeoLite2-ASN-Test.json ├── GeoLite2-City-Test.json └── GeoLite2-Country-Test.json ├── test-data ├── GeoIP-Anonymous-Plus-Test.mmdb ├── GeoIP2-Anonymous-IP-Test.mmdb ├── GeoIP2-City-Shield-Test.mmdb ├── GeoIP2-City-Test-Broken-Double-Format.mmdb ├── GeoIP2-City-Test-Invalid-Node-Count.mmdb ├── GeoIP2-City-Test.mmdb ├── GeoIP2-Connection-Type-Test.mmdb ├── GeoIP2-Country-Shield-Test.mmdb ├── GeoIP2-Country-Test.mmdb ├── GeoIP2-DensityIncome-Test.mmdb ├── GeoIP2-Domain-Test.mmdb ├── GeoIP2-Enterprise-Shield-Test.mmdb ├── GeoIP2-Enterprise-Test.mmdb ├── GeoIP2-IP-Risk-Test.mmdb ├── GeoIP2-ISP-Test.mmdb ├── GeoIP2-Precision-Enterprise-Shield-Test.mmdb ├── GeoIP2-Precision-Enterprise-Test.mmdb ├── GeoIP2-Static-IP-Score-Test.mmdb ├── GeoIP2-User-Count-Test.mmdb ├── GeoLite2-ASN-Test.mmdb ├── GeoLite2-City-Test.mmdb ├── GeoLite2-Country-Test.mmdb ├── MaxMind-DB-no-ipv4-search-tree.mmdb ├── MaxMind-DB-string-value-entries.mmdb ├── MaxMind-DB-test-broken-pointers-24.mmdb ├── MaxMind-DB-test-broken-search-tree-24.mmdb ├── MaxMind-DB-test-decoder.mmdb ├── MaxMind-DB-test-ipv4-24.mmdb ├── MaxMind-DB-test-ipv4-28.mmdb ├── MaxMind-DB-test-ipv4-32.mmdb ├── MaxMind-DB-test-ipv6-24.mmdb ├── MaxMind-DB-test-ipv6-28.mmdb ├── MaxMind-DB-test-ipv6-32.mmdb ├── MaxMind-DB-test-metadata-pointers.mmdb ├── MaxMind-DB-test-mixed-24.mmdb ├── MaxMind-DB-test-mixed-28.mmdb ├── MaxMind-DB-test-mixed-32.mmdb ├── MaxMind-DB-test-nested.mmdb ├── MaxMind-DB-test-pointer-decoder.mmdb ├── README.md └── maps-with-pointers.raw └── tidyall.ini /.gitattributes: -------------------------------------------------------------------------------- 1 | *.mmdb diff=mmdb 2 | -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | [diff "mmdb"] 2 | textconv = hexdump -v -C 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "Code scanning - action" 2 | 3 | on: 4 | pull_request: 5 | schedule: 6 | - cron: '0 11 * * 2' 7 | 8 | permissions: 9 | security-events: write # Used by this action. 10 | 11 | jobs: 12 | CodeQL-Build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | with: 20 | # We must fetch at least the immediate parents so that if this is 21 | # a pull request then we can checkout the head. 22 | fetch-depth: 2 23 | persist-credentials: false 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v3 33 | with: 34 | languages: go 35 | 36 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 37 | # If this step fails, then you should remove it and run the build manually (see below) 38 | - name: Autobuild 39 | uses: github/codeql-action/autobuild@v3 40 | 41 | # ℹ️ Command-line programs to run using the OS shell. 42 | # 📚 https://git.io/JvXDl 43 | 44 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 45 | # and modify them (or add more) to build your code if your project 46 | # uses a compiled language 47 | 48 | #- run: | 49 | # make bootstrap 50 | # make release 51 | 52 | - name: Perform CodeQL Analysis 53 | uses: github/codeql-action/analyze@v3 54 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '6 15 * * SUN' 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | 13 | build: 14 | strategy: 15 | matrix: 16 | go-version: [1.23.x, 1.24.x] 17 | # We don't test on macOS and windows as the database builds aren't 18 | # repeatable there for some reason. As such, tests fail. It'd 19 | # probably be worth looking into this at some point. 20 | platform: [ubuntu-latest] 21 | runs-on: ${{ matrix.platform }} 22 | name: "Build ${{ matrix.go-version }} test on ${{ matrix.platform }}" 23 | steps: 24 | - name: Set up Go 1.x 25 | uses: actions/setup-go@v5 26 | with: 27 | go-version: ${{ matrix.go-version }} 28 | id: go 29 | 30 | - name: Check out code into the Go module directory 31 | uses: actions/checkout@v4 32 | with: 33 | persist-credentials: false 34 | 35 | - name: Get dependencies 36 | run: go get -v -t -d ./... 37 | 38 | - name: Build 39 | run: go build -v ./... 40 | 41 | - name: Test 42 | run: go test -race -v ./... 43 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '6 15 * * SUN' 8 | 9 | permissions: {} 10 | 11 | jobs: 12 | golangci: 13 | name: lint 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/setup-go@v5 17 | with: 18 | go-version: 1.23 19 | - uses: actions/checkout@v4 20 | with: 21 | persist-credentials: false 22 | - name: golangci-lint 23 | uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # 8.0.0 24 | with: 25 | version: latest 26 | -------------------------------------------------------------------------------- /.github/workflows/zizmor.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions Security Analysis with zizmor 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["**"] 8 | 9 | jobs: 10 | zizmor: 11 | name: zizmor latest via PyPI 12 | runs-on: ubuntu-latest 13 | permissions: 14 | security-events: write 15 | # required for workflows in private repositories 16 | contents: read 17 | actions: read 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v4 21 | with: 22 | persist-credentials: false 23 | 24 | - name: Install the latest version of uv 25 | uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # 6.1.0 26 | with: 27 | enable-cache: false 28 | 29 | - name: Run zizmor 30 | run: uvx zizmor@1.7.0 --format plain . 31 | env: 32 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_site 2 | .tidyall.d 3 | *.swp 4 | /cmd/write-test-data/write-test-data 5 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | # This is needed for precious, which may run multiple instances 4 | # in parallel 5 | go: "1.23" 6 | tests: true 7 | allow-parallel-runners: true 8 | linters: 9 | default: all 10 | disable: 11 | # The canonical form is not always the most common form for some headers 12 | # and there is a small chance that switching existing strings could 13 | # break something. 14 | - canonicalheader 15 | 16 | - cyclop 17 | 18 | # This forbids stuff like "_, _, _, err := ...". Although I agree that it 19 | # is not ideal, the actual uses in our code-base are mostly due to third- 20 | # party libraries. 21 | - dogsled 22 | 23 | # This seems to primarily go off for table-driven tests for us. I don't 24 | # know if more sharing between these tests would actually make the code 25 | # easier to follow. 26 | - dupl 27 | 28 | # This is very helpful for linting comments, but for strings in code it 29 | # is pretty questionable, e.g., we have repeated strings in GeoIP build 30 | # code for org names or in test cases. We should consider enabling if 31 | # they allow limiting it to certain cases in the future. 32 | - dupword 33 | 34 | # We don't follow its policy about not defining dynamic errors. 35 | - err113 36 | 37 | # We often don't initialize all of the struct fields. This is fine 38 | # generally 39 | - exhaustruct 40 | 41 | # We tried this linter but most places we do forced type asserts are 42 | # pretty safe, e.g., an atomic.Value when everything is encapsulated 43 | # in a small package. 44 | - forcetypeassert 45 | 46 | - funlen 47 | - gochecknoglobals 48 | - gochecknoinits 49 | 50 | # Similar to the exhaustive linter and I don't know that we use these 51 | # sorts of sum types 52 | - gochecksumtype 53 | 54 | - gocognit 55 | 56 | # We don't want to forbid TODO, FIXME, etc. 57 | - godox 58 | 59 | # This only "caught" one thing, and it seemed like a reasonable use 60 | # of Han script. Generally, I don't think we want to prevent the use 61 | # of particular scripts. The time.Local checks might be useful, but 62 | # this didn't actually catch anything of note there. 63 | - gosmopolitan 64 | 65 | # Seems too opinionated or at least would require going through all the 66 | # interfaces we have. 67 | - inamedparam 68 | 69 | # This is an ok rule generally, but we don't return that many interfaces 70 | # and where we do, we tend to have a particular reason to do so. 71 | - ireturn 72 | 73 | # We use golines instead. 74 | - lll 75 | 76 | # Maintainability Index. Seems like it could be a good idea, but a 77 | # lot of things fail and we would need to make some decisions about 78 | # what to allow. 79 | - maintidx 80 | 81 | # Using a const for every number doesn't necessarily increase code clarity, 82 | # and it would be a ton of work to move everything to that. 83 | - mnd 84 | 85 | # Causes panics, e.g., when processing mmerrors 86 | - musttag 87 | 88 | - nestif 89 | 90 | # We do end up with a lot of debates on PRs about nil, nil returns. Such 91 | # returns are often surprising, but there seem to be enough valid cases 92 | # in our codebase that I am reluctant to enforce this. 93 | - nilnil 94 | 95 | # Checks for a new line before a return. Although that often seems helpful, 96 | # there are many cases where it is not. 97 | - nlreturn 98 | 99 | # We occasionally use named returns for documentation, which is helpful. 100 | # Named returns are only really a problem when used in conjunction with 101 | # a bare return statement. I _think_ Revive's bare-return covers that 102 | # case. 103 | - nonamedreturns 104 | 105 | # This is not something we want to enforce throughout our code. 106 | - paralleltest 107 | 108 | # This seems like premature optimization for most programs. 109 | - prealloc 110 | 111 | # We have very few structs with multiple tags and for the couple we had, this 112 | # actually made it harder to read. 113 | - tagalign 114 | # We don't follow this. Sometimes we test internal code. 115 | - testpackage 116 | # We probably _should_ be doing this! 117 | - thelper 118 | - varnamelen 119 | 120 | # Although some fixes in this seem good (e.g., err cuddling), I was 121 | # not able to disable some of the less desirable whitespace changes. 122 | - wsl 123 | 124 | settings: 125 | # Please note that we only use depguard for blocking packages and 126 | # gomodguard for blocking modules. 127 | depguard: 128 | rules: 129 | main: 130 | deny: 131 | - pkg: github.com/likexian/gokit/assert 132 | desc: Use github.com/stretchr/testify/assert 133 | - pkg: golang.org/x/exp/maps 134 | desc: Use maps instead. 135 | - pkg: golang.org/x/exp/slices 136 | desc: Use slices instead. 137 | - pkg: golang.org/x/exp/slog 138 | desc: Use log/slog instead. 139 | - pkg: google.golang.org/api/compute/v1 140 | desc: Use cloud.google.com/go/compute/apiv1 instead. 141 | - pkg: google.golang.org/api/cloudresourcemanager/v1 142 | desc: Use cloud.google.com/go/resourcemanager/apiv3 instead. 143 | - pkg: google.golang.org/api/serviceusage/v1 144 | desc: Use cloud.google.com/go/serviceusage/apiv1 instead. 145 | - pkg: io/ioutil 146 | desc: Deprecated. Functions have been moved elsewhere. 147 | - pkg: k8s.io/utils/strings/slices 148 | desc: Use slices 149 | - pkg: math/rand$ 150 | desc: Use math/rand/v2 or crypto/rand as appropriate. 151 | - pkg: sort 152 | desc: Use slices instead 153 | 154 | errcheck: 155 | # Don't allow setting of error to the blank identifier. If there is a legitimate 156 | # reason, there should be a nolint with an explanation. 157 | check-blank: true 158 | exclude-functions: 159 | # If we are rolling back a transaction, we are often already in an error 160 | # state. 161 | - (*database/sql.Tx).Rollback 162 | 163 | # It is reasonable to ignore errors if Cleanup fails in most cases. 164 | - (*github.com/google/renameio/v2.PendingFile).Cleanup 165 | 166 | # We often do not care if unlocking failed as we are exiting anyway. 167 | - (*github.com/gofrs/flock.Flock).Unlock 168 | 169 | # We often don't care if removing a file failed (e.g., it doesn't exist) 170 | - os.Remove 171 | - os.RemoveAll 172 | 173 | errchkjson: 174 | report-no-exported: true 175 | 176 | errorlint: 177 | errorf: true 178 | asserts: true 179 | comparison: true 180 | 181 | exhaustive: 182 | default-signifies-exhaustive: true 183 | 184 | forbidigo: 185 | # Forbid the following identifiers 186 | forbid: 187 | - pattern: ^atomic.Value$ 188 | pkg: ^sync/atomic$ 189 | msg: Use atomic.Pointer instead. 190 | - pattern: GeoFeed 191 | msg: you should use the `Geofeed` qualifier instead 192 | - pattern: Geoip 193 | msg: you should use the `GeoIP` qualifier instead 194 | - pattern: geoIP 195 | msg: you should use the `geoip` qualifier instead 196 | - pattern: ^hubSpot 197 | msg: you should use the `hubspot` qualifier instead 198 | - pattern: Maxmind 199 | msg: you should use the `MaxMind` qualifier instead 200 | - pattern: ^maxMind 201 | msg: you should use the `maxmind` qualifier instead 202 | - pattern: Minfraud 203 | msg: you should use the `MinFraud` qualifier instead 204 | - pattern: ^minFraud 205 | msg: you should use the `minfraud` qualifier instead 206 | - pattern: "[Uu]ser[iI][dD]" 207 | msg: you should use the `accountID` or the `AccountID` qualifier instead 208 | - pattern: WithEnterpriseURLs 209 | msg: Use ghe.NewClient instead. 210 | - pattern: ^bigquery.NewClient 211 | msg: you should use mmgcloud.NewBigQueryClient instead. 212 | - pattern: ^drive.NewService 213 | msg: you should use mmgdrive.NewGDrive instead. 214 | - pattern: ^filepath.Walk$ 215 | msg: you should use filepath.WalkDir instead as it doesn't call os.Lstat on every entry. 216 | - pattern: ^math.Min$ 217 | msg: you should use the min built-in instead. 218 | - pattern: ^mux.Vars$ 219 | msg: use req.PathValue instead. 220 | - pattern: ^net.ParseCIDR 221 | msg: you should use netip.ParsePrefix unless you really need a *net.IPNet 222 | - pattern: ^net.ParseIP 223 | msg: you should use netip.ParseAddr unless you really need a net.IP 224 | - pattern: ^pgtype.NewMap 225 | msg: you should use mmdatabase.NewTypeMap instead 226 | - pattern: ^sheets.NewService 227 | msg: you should use mmgcloud.NewSheetsService instead. 228 | - pattern: ^storage.NewClient 229 | msg: you should use gstorage.NewClient instead. This sets the HTTP client settings that we need for internal use. 230 | - pattern: ^os.IsNotExist 231 | msg: As per their docs, new code should use errors.Is(err, fs.ErrNotExist). 232 | - pattern: ^os.IsExist 233 | msg: As per their docs, new code should use errors.Is(err, fs.ErrExist) 234 | - pattern: ^net.LookupIP 235 | msg: You should use net.Resolver functions instead. 236 | - pattern: ^net.LookupCNAME 237 | msg: You should use net.Resolver functions instead. 238 | - pattern: ^net.LookupHost 239 | msg: You should use net.Resolver functions instead. 240 | - pattern: ^net.LookupPort 241 | msg: You should use net.Resolver functions instead. 242 | - pattern: ^net.LookupTXT 243 | msg: You should use net.Resolver functions instead. 244 | - pattern: ^net.LookupAddr 245 | msg: You should use net.Resolver functions instead. 246 | - pattern: ^net.LookupMX 247 | msg: You should use net.Resolver functions instead. 248 | - pattern: ^net.LookupNS 249 | msg: You should use net.Resolver functions instead. 250 | - pattern: ^net.LookupSRV 251 | msg: You should use net.Resolver functions instead. 252 | 253 | gocritic: 254 | enable-all: true 255 | disabled-checks: 256 | # Revive's defer rule already captures this. This caught no extra cases. 257 | - deferInLoop 258 | 259 | # Given that all of our code runs on Linux and the / separate should 260 | # work fine, this seems less important. 261 | - filepathJoin 262 | 263 | # This might be good, but we would have to revisit a lot of code. 264 | - hugeParam 265 | 266 | # This might be good, but I don't think we want to encourage 267 | # significant changes to regexes as we port stuff from Perl. 268 | - regexpSimplify 269 | 270 | # This seems like it might also be good, but a lot of existing code 271 | # fails. 272 | - sloppyReassign 273 | 274 | # I am not sure we would want this linter and a lot of existing 275 | # code fails. 276 | - unnamedResult 277 | 278 | # Covered by nolintlint 279 | - whyNoLint 280 | 281 | gomoddirectives: 282 | replace-allow-list: 283 | # We want to use an old version due to race conditions in the latest 284 | # release. 285 | - github.com/fsnotify/fsnotify 286 | # We want to use an old version due to a logging issue. 287 | - github.com/snowflakedb/gosnowflake 288 | toolchain-forbidden: true 289 | go-version-pattern: \d\.\d+(\.0)? 290 | 291 | # IMPORTANT: gomodguard blocks _modules_, not arbitrary packages. Be 292 | # sure to use the module path from the go.mod file for these. 293 | # See https://github.com/ryancurrah/gomodguard/issues/12 294 | gomodguard: 295 | blocked: 296 | modules: 297 | - github.com/avct/uasurfer: 298 | recommendations: 299 | - github.com/xavivars/uasurfer 300 | reason: The original avct module appears abandoned. 301 | - github.com/BurntSushi/toml: 302 | recommendations: 303 | - github.com/pelletier/go-toml/v2 304 | reason: This library panics frequently on invalid input. 305 | - github.com/pelletier/go-toml: 306 | recommendations: 307 | - github.com/pelletier/go-toml/v2 308 | reason: This is an outdated version. 309 | - github.com/gofrs/uuid: 310 | recommendations: 311 | - github.com/google/uuid 312 | - github.com/gofrs/uuid/v5: 313 | recommendations: 314 | - github.com/google/uuid 315 | - github.com/satori/go.uuid: 316 | recommendations: 317 | - github.com/google/uuid 318 | - github.com/google/uuid: 319 | recommendations: 320 | - github.com/google/uuid 321 | - github.com/lib/pq: 322 | recommendations: 323 | - github.com/jackc/pgx 324 | reason: This library is no longer actively maintained. 325 | - github.com/neilotoole/errgroup: 326 | recommendations: 327 | - golang.org/x/sync/errgroup 328 | reason: This library can lead to subtle deadlocks in certain use cases. 329 | - github.com/pariz/gountries: 330 | reason: This library's data is not actively maintained. Use GeoInfo data. 331 | github.com/pkg/errors: 332 | recommendations: 333 | - errors 334 | reason: pkg/errors is no longer maintained. 335 | - github.com/RackSec/srslog: 336 | recommendations: 337 | - log/syslog 338 | reason: This library's data is not actively maintained. 339 | - github.com/ua-parser/uap-go: 340 | recommendations: 341 | - github.com/xavivars/uasurfer 342 | reason: The performance of this library is absolutely abysmal. 343 | - github.com/ugorji/go: 344 | recommendations: 345 | - encoding/json 346 | - github.com/mailru/easyjson 347 | reason: This library is poorly maintained. We should default to using encoding/json and use easyjson where performance really matters. 348 | - github.com/zeebo/assert: 349 | recommendations: 350 | - github.com/stretchr/testify/assert 351 | reason: Use github.com/stretchr/testify/assert 352 | - gopkg.in/yaml.v2: 353 | recommendations: 354 | - github.com/goccy/go-yaml 355 | reason: Not actively maintained. 356 | - gopkg.in/yaml.v3: 357 | recommendations: 358 | - github.com/goccy/go-yaml 359 | reason: Not actively maintained. 360 | - gotest.tools/v3: 361 | recommendations: 362 | - github.com/stretchr/testify/assert 363 | reason: Use github.com/stretchr/testify/assert 364 | - inet.af/netaddr: 365 | recommendations: 366 | - net/netip 367 | - go4.org/netipx 368 | reason: inet.af/netaddr has been deprecated. 369 | versions: 370 | - github.com/jackc/pgconn: 371 | reason: Use github.com/jackc/pgx/v5 372 | - github.com/jackc/pgtype: 373 | reason: Use github.com/jackc/pgx/v5 374 | - github.com/jackc/pgx: 375 | version: < 5.0.0 376 | reason: Use github.com/jackc/pgx/v5 377 | 378 | gosec: 379 | excludes: 380 | # G104 - "Audit errors not checked." We use errcheck for this. 381 | - G104 382 | 383 | # G306 - "Expect WriteFile permissions to be 0600 or less". 384 | - G306 385 | 386 | # Prohibits defer (*os.File).Close, which we allow when reading from file. 387 | - G307 388 | 389 | # We use md5 in geoipupdate 390 | - G401 391 | - G501 392 | 393 | # no longer relevant with 1.22 394 | - G601 395 | 396 | govet: 397 | disable: 398 | # Although it is very useful in particular cases where we are trying to 399 | # use as little memory as possible, there are even more cases where 400 | # other organizations may make more sense. 401 | - fieldalignment 402 | enable-all: true 403 | settings: 404 | shadow: 405 | strict: true 406 | 407 | loggercheck: 408 | # although it seems like this should remove the need for our custom 409 | # Ruleguard check, it misses things that seems to catch. However, it 410 | # is possible that this will catch things that misses as that check 411 | # is very simple. 412 | no-printf-like: true 413 | 414 | misspell: 415 | locale: US 416 | extra-words: 417 | - typo: marshall 418 | correction: marshal 419 | - typo: marshalling 420 | correction: marshaling 421 | - typo: marshalls 422 | correction: marshals 423 | - typo: unmarshall 424 | correction: unmarshal 425 | - typo: unmarshalling 426 | correction: unmarshaling 427 | - typo: unmarshalls 428 | correction: unmarshals 429 | 430 | nolintlint: 431 | require-explanation: true 432 | require-specific: true 433 | allow-no-explanation: 434 | - misspell 435 | # Setting allow-unused to false would be nice. We previously had it that 436 | # way. However there is an unresolved issue where it occasionally fails 437 | # when it should not. We think there might be some kind of caching bug in 438 | # golangci-lint. See 439 | # https://maxmind.slack.com/archives/C07ER81BQ4T/p1739389216305519. 440 | allow-unused: false 441 | 442 | revive: 443 | severity: warning 444 | enable-all-rules: true 445 | rules: 446 | # This might be nice but it is so common that it is hard 447 | # to enable. 448 | - name: add-constant 449 | disabled: true 450 | 451 | - name: argument-limit 452 | disabled: true 453 | 454 | - name: cognitive-complexity 455 | disabled: true 456 | 457 | - name: comment-spacings 458 | arguments: 459 | - easyjson 460 | - nolint 461 | disabled: false 462 | 463 | # Probably a good rule, but we have a lot of names that 464 | # only have case differences. 465 | - name: confusing-naming 466 | disabled: true 467 | 468 | - name: cyclomatic 469 | disabled: true 470 | 471 | # Although being consistent might be nice, I don't know that it 472 | # is worth the effort enabling this rule. It doesn't have an 473 | # autofix option. 474 | - name: enforce-repeated-arg-type-style 475 | arguments: 476 | - short 477 | disabled: true 478 | 479 | - name: enforce-map-style 480 | arguments: 481 | - literal 482 | disabled: false 483 | 484 | # We have very few of these as we force nil slices in most places, 485 | # but there are a couple of cases. 486 | - name: enforce-slice-style 487 | arguments: 488 | - literal 489 | disabled: false 490 | 491 | - name: file-header 492 | disabled: true 493 | 494 | # We have a lot of flag parameters. This linter probably makes 495 | # a good point, but we would need some cleanup or a lot of nolints. 496 | - name: flag-parameter 497 | disabled: true 498 | 499 | - name: function-length 500 | disabled: true 501 | 502 | - name: function-result-limit 503 | disabled: true 504 | 505 | - name: line-length-limit 506 | disabled: true 507 | 508 | - name: max-public-structs 509 | disabled: true 510 | 511 | # We frequently use nested structs, particularly in tests. 512 | - name: nested-structs 513 | disabled: true 514 | 515 | # This doesn't make sense with 1.22 loop var changes. 516 | - name: range-val-address 517 | disabled: true 518 | 519 | # This flags things that do not seem like a problem, e.g. "sixHours". 520 | - name: time-naming 521 | disabled: true 522 | 523 | # This causes a ton of failures. Many are fairly safe. It might be nice to 524 | # enable, but probably not worth the effort. 525 | - name: unchecked-type-assertion 526 | disabled: true 527 | 528 | # This seems to give many false positives. 529 | - name: unconditional-recursion 530 | disabled: true 531 | 532 | # This is covered elsewhere and we want to ignore some 533 | # functions such as fmt.Fprintf. 534 | - name: unhandled-error 535 | disabled: true 536 | 537 | # We generally have unused receivers in tests for meeting the 538 | # requirements of an interface. 539 | - name: unused-receiver 540 | disabled: true 541 | 542 | sloglint: 543 | # Enforce not mixing key-value pairs and attributes. 544 | no-mixed-args: true 545 | 546 | # Enforce not using global loggers. 547 | no-global: all 548 | 549 | # Enforce a snake case for keys. 550 | key-naming-case: snake 551 | 552 | # Make sure we don't use reserved keys. 553 | forbidden-keys: 554 | # These are included by our handler. 555 | - time 556 | - level 557 | - logged_from 558 | - message 559 | 560 | # We don't use these two, but they are slog defaults. It would 561 | # be better to avoid using them to reduce confusion and to make 562 | # it easier to potentially use the defaults in the future. 563 | - msg 564 | - source 565 | 566 | staticcheck: 567 | checks: 568 | - all 569 | 570 | # SA1019: Using a deprecated function, variable, constant or field 571 | # 572 | # This is disabled as it interacts poorly with golangci-lint's caching. 573 | # I believe https://github.com/golangci/golangci-lint-action/issues/420 574 | # is the same underlying issue. 575 | - -SA1019 576 | 577 | # SA5008: unknown JSON option "intern" - easyjson specific option. 578 | - -SA5008 579 | 580 | tagliatelle: 581 | case: 582 | rules: 583 | avro: snake 584 | bson: snake 585 | env: upperSnake 586 | envconfig: upperSnake 587 | json: snake 588 | mapstructure: snake 589 | xml: snake 590 | yaml: snake 591 | 592 | testifylint: 593 | enable-all: true 594 | 595 | unparam: 596 | check-exported: true 597 | 598 | usestdlibvars: 599 | time-layout: true 600 | 601 | usetesting: 602 | os-temp-dir: true 603 | 604 | wrapcheck: 605 | ignore-sigs: 606 | - .Errorf( 607 | - errgroup.NewMultiError( 608 | - errors.Join( 609 | - errors.New( 610 | - .Wait( 611 | - .WithStack( 612 | - .Wrap( 613 | - .Wrapf( 614 | - v5.Retry[T any]( 615 | 616 | exclusions: 617 | generated: lax 618 | rules: 619 | # This rule doesn't really make sense for tests where we don't have an open 620 | # connection and we might be passing around the response for other reasons. 621 | - linters: 622 | - bodyclose 623 | path: _test.go 624 | 625 | # There are many cases where we want to just close resources and ignore the 626 | # error (e.g., for defer f.Close on a read). errcheck removed its built-in 627 | # wildcard ignore. I tried listing all of the cases, but it was too many 628 | # and some were very specific. 629 | - linters: 630 | - errcheck 631 | source: \.Close 632 | 633 | # This refers to a minFraud field, not the MaxMind Account ID 634 | - linters: 635 | - forbidigo 636 | source: "[Aa]ccountUserID|Account\\.UserID" 637 | 638 | # we include both a source and text exclusion as the source exclusion 639 | # misses matches where forbidigo reports the error on the first line 640 | # of a chunk of a function call even though the use is on a later line. 641 | - linters: 642 | - forbidigo 643 | text: "[Aa]ccountUserID|Account\\.UserID" 644 | 645 | # For some reason the imports stuff in ruleguard doesn't work in golangci-lint. 646 | # Perhaps it has an outdated version or something 647 | - linters: 648 | - gocritic 649 | path: _test.go 650 | text: "ruleguard: Prefer the alternative Context method instead" 651 | 652 | # The nolintlint linter behaves oddly with ruleguard rules 653 | - linters: 654 | - gocritic 655 | source: // *no-ruleguard 656 | 657 | # The contextcheck linter also uses "nolint" in a slightly different way, 658 | # leading to falso positives from nolintlint. 659 | - linters: 660 | - nolintlint 661 | source: //nolint:contextcheck //.* 662 | 663 | # These are usually fine to shadow and not allowing shadowing for them can 664 | # make the code unnecessarily verbose. 665 | - linters: 666 | - govet 667 | text: 'shadow: declaration of "(ctx|err|ok)" shadows declaration' 668 | 669 | - linters: 670 | - contextcheck 671 | # With recent changes to the linter, there were a lot of failures in 672 | # the tests and it wasn't clear to me that fixing them would actually 673 | # improve the readability. 674 | - goconst 675 | - nilerr 676 | - wrapcheck 677 | path: _test.go 678 | 679 | # ST1016 - methods on the same type should have the same receiver name. 680 | # easyjson doesn't interact well with this. 681 | - linters: 682 | - staticcheck 683 | text: ST1016 684 | 685 | - linters: 686 | - wrapcheck 687 | text: github.com/maxmind/MaxMind-DB 688 | 689 | - linters: 690 | - wrapcheck 691 | path: _easyjson.go 692 | 693 | - linters: 694 | - gocritic 695 | text: octalLiteral 696 | source: Chmod|WriteFile 697 | 698 | paths: 699 | - _easyjson\.go$ 700 | - _easyjson_test\.go$ 701 | - _xgb2code\.go$ 702 | - _json2vector\.go$ 703 | - geoip-build/mmcsv 704 | 705 | formatters: 706 | enable: 707 | - gci 708 | - gofumpt 709 | - goimports 710 | - golines 711 | 712 | settings: 713 | gci: 714 | sections: 715 | - standard 716 | - default 717 | - prefix(github.com/maxmind/MaxMind-DB) 718 | 719 | gofumpt: 720 | module-path: github.com/maxmind/MaxMind-DB 721 | extra-rules: true 722 | 723 | goimports: 724 | local-prefixes: 725 | - github.com/maxmind/MaxMind-DB 726 | 727 | golines: 728 | shorten-comments: true 729 | 730 | exclusions: 731 | generated: lax 732 | paths: 733 | - _easyjson\.go$ 734 | - _easyjson_test\.go$ 735 | - _xgb2code\.go$ 736 | - _json2vector\.go$ 737 | - geoip-build/mmcsv 738 | -------------------------------------------------------------------------------- /.tidyallrc: -------------------------------------------------------------------------------- 1 | [JSON] 2 | select = **/*.json 3 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | https://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | https://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the "Software"), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 5 | of the Software, and to permit persons to whom the Software is furnished to do 6 | so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /MaxMind-DB-spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: MaxMind DB File Format Specification 4 | version: v2.0 5 | --- 6 | # MaxMind DB File Format Specification 7 | 8 | ## Description 9 | 10 | The MaxMind DB file format is a database format that maps IPv4 and IPv6 11 | addresses to data records using an efficient binary search tree. 12 | 13 | ## Version 14 | 15 | This spec documents **version 2.0** of the MaxMind DB binary format. 16 | 17 | The version number consists of separate major and minor version numbers. It 18 | should not be considered a decimal number. In other words, version 2.10 comes 19 | after version 2.9. 20 | 21 | Code which is capable of reading a given major version of the format should 22 | not be broken by minor version changes to the format. 23 | 24 | ## Overview 25 | 26 | The binary database is split into three parts: 27 | 28 | 1. The binary search tree. Each level of the tree corresponds to a single bit 29 | in the prefix of the network the IP address belongs to. 30 | 2. The data section with the values for the networks in the binary search 31 | tree. These values may be comprised of a single data type, e.g., the string 32 | "US" or "New York", or they may be a more complex map or array type made up 33 | of multiple fields. 34 | 3. Database metadata. Information about the database itself. 35 | 36 | ## Database Metadata 37 | 38 | This portion of the database is stored at the end of the file. It is 39 | documented first because understanding some of the metadata is key to 40 | understanding how the other sections work. 41 | 42 | This section can be found by looking for a binary sequence matching 43 | "\xab\xcd\xefMaxMind.com". The *last* occurrence of this string in the file 44 | marks the end of the data section and the beginning of the metadata. Since we 45 | allow for arbitrary binary data in the data section, some other piece of data 46 | could contain these values. This is why you need to find the last occurrence 47 | of this sequence. 48 | 49 | The maximum allowable size for the metadata section, including the marker that 50 | starts the metadata, is 128KiB. 51 | 52 | The metadata is stored as a separate data section comprised of a map data 53 | structure starting at the beginning of that section. This structure is 54 | described later in the spec. 55 | 56 | Except where otherwise specified, each key listed is required for the database 57 | to be considered valid. 58 | 59 | Changing a key's data type or removing a key would constitute a major version 60 | change for this spec. Adding a key constitutes a minor version change. 61 | 62 | The list of known keys for the current version of the format is as follows: 63 | 64 | ### node\_count 65 | 66 | This is an unsigned 32-bit integer indicating the number of nodes in the 67 | search tree. 68 | 69 | ### record\_size 70 | 71 | This is an unsigned 16-bit integer. It indicates the number of bits in a 72 | record in the search tree. Note that each node consists of *two* records. 73 | 74 | ### ip\_version 75 | 76 | This is an unsigned 16-bit integer which is always 4 or 6. It indicates 77 | whether the database contains IPv4 or IPv6 address data. 78 | 79 | ### database\_type 80 | 81 | This is a string that indicates the structure of each data record associated 82 | with an IP address. The actual definition of these structures is left up to 83 | the database creator. 84 | 85 | Names starting with "GeoIP" are reserved for use by MaxMind (and "GeoIP" is a 86 | trademark anyway). 87 | 88 | ### languages 89 | 90 | An array of strings, each of which is a locale code. A given record may 91 | contain data items that have been localized to some or all of these 92 | locales. Records should not contain localized data for locales not included in 93 | this array. 94 | 95 | This is an optional key, as this may not be relevant for all types of data. 96 | 97 | ### binary\_format\_major\_version 98 | 99 | This is an unsigned 16-bit integer indicating the major version number for the 100 | database's binary format. 101 | 102 | ### binary\_format\_minor\_version 103 | 104 | This is an unsigned 16-bit integer indicating the minor version number for the 105 | database's binary format. 106 | 107 | ### build\_epoch 108 | 109 | This is an unsigned 64-bit integer that contains the database build timestamp 110 | as a Unix epoch value. 111 | 112 | ### description 113 | 114 | This key will always point to a map. The keys of that map will be language 115 | codes, and the values will be a description in that language as a UTF-8 116 | string. 117 | 118 | The codes may include additional information such as script or country 119 | identifiers, like "zh-TW" or "mn-Cyrl-MN". The additional identifiers will be 120 | separated by a dash character ("-"). 121 | 122 | This key is optional. However, creators of databases are strongly 123 | encouraged to include a description in at least one language. 124 | 125 | ### Calculating the Search Tree Section Size 126 | 127 | The formula for calculating the search tree section size *in bytes* is as 128 | follows: 129 | 130 | ( ( $record_size * 2 ) / 8 ) * $number_of_nodes 131 | 132 | The end of the search tree marks the beginning of the data section. 133 | 134 | ## Binary Search Tree Section 135 | 136 | The database file starts with a binary search tree. The number of nodes in the 137 | tree is dependent on how many unique netblocks are needed for the particular 138 | database. For example, the city database needs many more small netblocks than 139 | the country database. 140 | 141 | The top most node is always located at the beginning of the search tree 142 | section's address space. The top node is node 0. 143 | 144 | Each node consists of two records, each of which is a pointer to an address in 145 | the file. 146 | 147 | The pointers can point to one of three things. First, it may point to another 148 | node in the search tree address space. These pointers are followed as part of 149 | the IP address search algorithm, described below. 150 | 151 | The pointer can point to a value equal to `$number_of_nodes`. If this is the 152 | case, it means that the IP address we are searching for is not in the 153 | database. 154 | 155 | Finally, it may point to an address in the data section. This is the data 156 | relevant to the given netblock. 157 | 158 | ### Node Layout 159 | 160 | Each node in the search tree consists of two records, each of which is a 161 | pointer. The record size varies by database, but inside a single database node 162 | records are always the same size. A record may be anywhere from 24 to 128 bits 163 | long, depending on the number of nodes in the tree. These pointers are 164 | stored in big-endian format (most significant byte first). 165 | 166 | Here are some examples of how the records are laid out in a node for 24, 28, 167 | and 32 bit records. Larger record sizes follow this same pattern. 168 | 169 | #### 24 bits (small database), one node is 6 bytes 170 | 171 | | <------------- node --------------->| 172 | | 23 .. 0 | 23 .. 0 | 173 | 174 | #### 28 bits (medium database), one node is 7 bytes 175 | 176 | | <------------- node --------------->| 177 | | 23 .. 0 | 27..24 | 27..24 | 23 .. 0 | 178 | 179 | Note 4 bits of each pointer are combined into the middle byte. For both 180 | records, they are prepended and end up in the most significant position. 181 | 182 | #### 32 bits (large database), one node is 8 bytes 183 | 184 | | <------------- node --------------->| 185 | | 31 .. 0 | 31 .. 0 | 186 | 187 | ### Search Lookup Algorithm 188 | 189 | The first step is to convert the IP address to its big-endian binary 190 | representation. For an IPv4 address, this becomes 32 bits. For IPv6 you get 191 | 128 bits. 192 | 193 | The leftmost bit corresponds to the first node in the search tree. For each 194 | bit, a value of 0 means we choose the left record in a node, and a value of 1 195 | means we choose the right record. 196 | 197 | The record value is always interpreted as an unsigned integer. The maximum 198 | size of the integer is dependent on the number of bits in a record (24, 28, or 199 | 32). 200 | 201 | If the record value is a number that is less than the *number of nodes* (not 202 | in bytes, but the actual node count) in the search tree (this is stored in the 203 | database metadata), then the value is a node number. In this case, we find 204 | that node in the search tree and repeat the lookup algorithm from there. 205 | 206 | If the record value is equal to the number of nodes, that means that we do not 207 | have any data for the IP address, and the search ends here. 208 | 209 | If the record value is *greater* than the number of nodes in the search tree, 210 | then it is an actual pointer value pointing into the data section. The value 211 | of the pointer is relative to the start of the data section, *not* the 212 | start of the file. 213 | 214 | In order to determine where in the data section we should start looking, we use 215 | the following formula: 216 | 217 | $data_section_offset = ( $record_value - $node_count ) - 16 218 | 219 | The 16 is the size of the data section separator. We subtract it because we 220 | want to permit pointing to the first byte of the data section. Recall that 221 | the record value cannot equal the node count as that means there is no 222 | data. Instead, we choose to start values that go to the data section at 223 | `$node_count + 16`. (This has the side effect that record values 224 | `$node_count + 1` through `$node_count + 15` inclusive are not valid). 225 | 226 | This is best demonstrated by an example: 227 | 228 | Let's assume we have a 24-bit tree with 1,000 nodes. Each node contains 48 229 | bits, or 6 bytes. The size of the tree is 6,000 bytes. 230 | 231 | When a record in the tree contains a number that is less than 1,000, this 232 | is a *node number*, and we look up that node. If a record contains a value 233 | greater than or equal to 1,016, we know that it is a data section value. We 234 | subtract the node count (1,000) and then subtract 16 for the data section 235 | separator, giving us the number 0, the first byte of the data section. 236 | 237 | If a record contained the value 6,000, this formula would give us an offset of 238 | 4,984 into the data section. 239 | 240 | In order to determine where in the file this offset really points to, we also 241 | need to know where the data section starts. This can be calculated by 242 | determining the size of the search tree in bytes and then adding an additional 243 | 16 bytes for the data section separator: 244 | 245 | $offset_in_file = $data_section_offset 246 | + $search_tree_size_in_bytes 247 | + 16 248 | 249 | Since we subtract and then add 16, the final formula to determine the 250 | offset in the file can be simplified to: 251 | 252 | $offset_in_file = ( $record_value - $node_count ) 253 | + $search_tree_size_in_bytes 254 | 255 | ### IPv4 addresses in an IPv6 tree 256 | 257 | When storing IPv4 addresses in an IPv6 tree, they are stored as-is, so they 258 | occupy the first 32-bits of the address space (from 0 to 2**32 - 1). 259 | 260 | Creators of databases should decide on a strategy for handling the various 261 | mappings between IPv4 and IPv6. 262 | 263 | The strategy that MaxMind uses for its GeoIP databases is to include a pointer 264 | from the `::ffff:0:0/96` subnet to the root node of the IPv4 address space in 265 | the tree. This accounts for the 266 | [IPv4-mapped IPv6 address](http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses). 267 | 268 | MaxMind also includes a pointer from the `2002::/16` subnet to the root node 269 | of the IPv4 address space in the tree. This accounts for the 270 | [6to4 mapping](http://en.wikipedia.org/wiki/6to4) subnet. 271 | 272 | Database creators are encouraged to document whether they are doing something 273 | similar for their databases. 274 | 275 | The Teredo subnet cannot be accounted for in the tree. Instead, code that 276 | searches the tree can offer to decode the IPv4 portion of a Teredo address and 277 | look that up. 278 | 279 | ## Data Section Separator 280 | 281 | There are 16 bytes of NULLs in between the search tree and the data 282 | section. This separator exists in order to make it possible for a verification 283 | tool to distinguish between the two sections. 284 | 285 | This separator is not considered part of the data section itself. In other 286 | words, the data section starts at `$size_of_search_tree + 16` bytes in the 287 | file. 288 | 289 | ## Output Data Section 290 | 291 | Each output data field has an associated type, and that type is encoded as a 292 | number that begins the data field. Some types are variable length. In those 293 | cases, the type indicator is also followed by a length. The data payload 294 | always comes at the end of the field. 295 | 296 | All binary data is stored in big-endian format. 297 | 298 | Note that the *interpretation* of a given data type's meaning is decided by 299 | higher-level APIs, not by the binary format itself. 300 | 301 | ### pointer - 1 302 | 303 | A pointer to another part of the data section's address space. The pointer 304 | will point to the beginning of a field. It is illegal for a pointer to point 305 | to another pointer. 306 | 307 | Pointer values start from the beginning of the data section, *not* the 308 | beginning of the file. Pointers in the metadata start from the beginning of 309 | the metadata section. 310 | 311 | ### UTF-8 string - 2 312 | 313 | A variable length byte sequence that contains valid utf8. If the length is 314 | zero then this is an empty string. 315 | 316 | ### double - 3 317 | 318 | This is stored as an IEEE-754 double (binary64) in big-endian format. The 319 | length of a double is always 8 bytes. 320 | 321 | ### bytes - 4 322 | 323 | A variable length byte sequence containing any sort of binary data. If the 324 | length is zero then this a zero-length byte sequence. 325 | 326 | This is not currently used but may be used in the future to embed non-text 327 | data (images, etc.). 328 | 329 | ### integer formats 330 | 331 | Integers are stored in variable length binary fields. 332 | 333 | We support 16-bit, 32-bit, 64-bit, and 128-bit unsigned integers. We also 334 | support 32-bit signed integers. 335 | 336 | A 128-bit integer can use up to 16 bytes, but may use fewer. Similarly, a 337 | 32-bit integer may use from 0-4 bytes. The number of bytes used is determined 338 | by the length specifier in the control byte. See below for details. 339 | 340 | A length of zero always indicates the number 0. 341 | 342 | When storing a signed integer, fields shorter than the maximum byte length 343 | are always positive. When the field is the maximum length, e.g., 4 bytes for 344 | 32-bit integers, the left-most bit is the sign. A 1 is negative and a 0 is 345 | positive. 346 | 347 | The type numbers for our integer types are: 348 | 349 | * unsigned 16-bit int - 5 350 | * unsigned 32-bit int - 6 351 | * signed 32-bit int - 8 352 | * unsigned 64-bit int - 9 353 | * unsigned 128-bit int - 10 354 | 355 | The unsigned 32-bit and 128-bit types may be used to store IPv4 and IPv6 356 | addresses, respectively. 357 | 358 | The signed 32-bit integers are stored using the 2's complement representation. 359 | 360 | ### map - 7 361 | 362 | A map data type contains a set of key/value pairs. Unlike other data types, 363 | the length information for maps indicates how many key/value pairs it 364 | contains, not its length in bytes. This size can be zero. 365 | 366 | See below for the algorithm used to determine the number of pairs in the 367 | hash. This algorithm is also used to determine the length of a field's 368 | payload. 369 | 370 | ### array - 11 371 | 372 | An array type contains a set of ordered values. The length information for 373 | arrays indicates how many values it contains, not its length in bytes. This 374 | size can be zero. 375 | 376 | This type uses the same algorithm as maps for determining the length of a 377 | field's payload. 378 | 379 | ### data cache container - 12 380 | 381 | This is a special data type that marks a container used to cache repeated 382 | data. For example, instead of repeating the string "United States" over and 383 | over in the database, we store it in the cache container and use pointers 384 | *into* this container instead. 385 | 386 | Nothing in the database will ever contain a pointer to this field 387 | itself. Instead, various fields will point into the container. 388 | 389 | The primary reason for making this a separate data type versus simply inlining 390 | the cached data is so that a database dumper tool can skip this cache when 391 | dumping the data section. The cache contents will end up being dumped as 392 | pointers into it are followed. 393 | 394 | ### end marker - 13 395 | 396 | The end marker marks the end of the data section. It is not strictly 397 | necessary, but including this marker allows a data section deserializer to 398 | process a stream of input, rather than having to find the end of the section 399 | before beginning the deserialization. 400 | 401 | This data type is not followed by a payload, and its size is always zero. 402 | 403 | ### boolean - 14 404 | 405 | A true or false value. The length information for a boolean type will always 406 | be 0 or 1, indicating the value. There is no payload for this field. 407 | 408 | ### float - 15 409 | 410 | This is stored as an IEEE-754 float (binary32) in big-endian format. The 411 | length of a float is always 4 bytes. 412 | 413 | This type is provided primarily for completeness. Because of the way floating 414 | point numbers are stored, this type can easily lose precision when serialized 415 | and then deserialized. If this is an issue for you, consider using a double 416 | instead. 417 | 418 | ### Data Field Format 419 | 420 | Each field starts with a control byte. This control byte provides information 421 | about the field's data type and payload size. 422 | 423 | The first three bits of the control byte tell you what type the field is. If 424 | these bits are all 0, then this is an "extended" type, which means that the 425 | *next* byte contains the actual type. Otherwise, the first three bits will 426 | contain a number from 1 to 7, the actual type for the field. 427 | 428 | We've tried to assign the most commonly used types as numbers 1-7 as an 429 | optimization. 430 | 431 | With an extended type, the type number in the second byte is the number 432 | minus 7. In other words, an array (type 11) will be stored with a 0 for the 433 | type in the first byte and a 4 in the second. 434 | 435 | Here is an example of how the control byte may combine with the next byte to 436 | tell us the type: 437 | 438 | 001XXXXX pointer 439 | 010XXXXX UTF-8 string 440 | 110XXXXX unsigned 32-bit int (ASCII) 441 | 000XXXXX 00000011 unsigned 128-bit int (binary) 442 | 000XXXXX 00000100 array 443 | 000XXXXX 00000110 end marker 444 | 445 | #### Payload Size 446 | 447 | The next five bits in the control byte tell you how long the data field's 448 | payload is, except for maps and pointers. Maps and pointers use this size 449 | information a bit differently. See below. 450 | 451 | If the five bits are smaller than 29, then those bits are the payload size in 452 | bytes. For example: 453 | 454 | 01000010 UTF-8 string - 2 bytes long 455 | 01011100 UTF-8 string - 28 bytes long 456 | 11000001 unsigned 32-bit int - 1 byte long 457 | 00000011 00000011 unsigned 128-bit int - 3 bytes long 458 | 459 | If the five bits are equal to 29, 30, or 31, then use the following algorithm 460 | to calculate the payload size. 461 | 462 | If the value is 29, then the size is 29 + *the next byte after the type 463 | specifying bytes as an unsigned integer*. 464 | 465 | If the value is 30, then the size is 285 + *the next two bytes after the type 466 | specifying bytes as a single unsigned integer*. 467 | 468 | If the value is 31, then the size is 65,821 + *the next three bytes after the 469 | type specifying bytes as a single unsigned integer*. 470 | 471 | Some examples: 472 | 473 | 01011101 00110011 UTF-8 string - 80 bytes long 474 | 475 | In this case, the last five bits of the control byte equal 29. We treat the 476 | next byte as an unsigned integer. The next byte is 51, so the total size is 477 | (29 + 51) = 80. 478 | 479 | 01011110 00110011 00110011 UTF-8 string - 13,392 bytes long 480 | 481 | The last five bits of the control byte equal 30. We treat the next two bytes 482 | as a single unsigned integer. The next two bytes equal 13,107, so the total 483 | size is (285 + 13,107) = 13,392. 484 | 485 | 01011111 00110011 00110011 00110011 UTF-8 string - 3,421,264 bytes long 486 | 487 | The last five bits of the control byte equal 31. We treat the next three bytes 488 | as a single unsigned integer. The next three bytes equal 3,355,443, so the 489 | total size is (65,821 + 3,355,443) = 3,421,264. 490 | 491 | This means that the maximum payload size for a single field is 16,843,036 492 | bytes. 493 | 494 | The binary number types always have a known size, but for consistency's sake, 495 | the control byte will always specify the correct size for these types. 496 | 497 | #### Maps 498 | 499 | Maps use the size in the control byte (and any following bytes) to indicate 500 | the number of key/value pairs in the map, not the size of the payload in 501 | bytes. 502 | 503 | This means that the maximum number of pairs for a single map is 16,843,036. 504 | 505 | Maps are laid out with each key followed by its value, followed by the next 506 | pair, etc. 507 | 508 | The keys are **always** UTF-8 strings. The values may be any data type, 509 | including maps or pointers. 510 | 511 | Once we know the number of pairs, we can look at each pair in turn to 512 | determine the size of the key and the key name, as well as the value's type 513 | and payload. 514 | 515 | #### Pointers 516 | 517 | Pointers use the last five bits in the control byte to calculate the pointer 518 | value. 519 | 520 | To calculate the pointer value, we start by subdividing the five bits into two 521 | groups. The first two bits indicate the size, and the next three bits are part 522 | of the value, so we end up with a control byte breaking down like this: 523 | 001SSVVV. 524 | 525 | The size can be 0, 1, 2, or 3. 526 | 527 | If the size is 0, the pointer is built by appending the next byte to the last 528 | three bits to produce an 11-bit value. 529 | 530 | If the size is 1, the pointer is built by appending the next two bytes to the 531 | last three bits to produce a 19-bit value + 2048. 532 | 533 | If the size is 2, the pointer is built by appending the next three bytes to the 534 | last three bits to produce a 27-bit value + 526336. 535 | 536 | Finally, if the size is 3, the pointer's value is contained in the next four 537 | bytes as a 32-bit value. In this case, the last three bits of the control byte 538 | are ignored. 539 | 540 | This means that we are limited to 4GB of address space for pointers, so the 541 | data section size for the database is limited to 4GB. 542 | 543 | ## Reference Implementations 544 | 545 | ### Writer 546 | 547 | * [Go](https://github.com/maxmind/mmdbwriter) 548 | 549 | ### Reader 550 | 551 | * [C](https://github.com/maxmind/libmaxminddb) 552 | * [C#](https://github.com/maxmind/MaxMind-DB-Reader-dotnet) 553 | * [Java](https://github.com/maxmind/MaxMind-DB-Reader-java) 554 | * [PHP](https://github.com/maxmind/MaxMind-DB-Reader-php) 555 | * [Python](https://github.com/maxmind/MaxMind-DB-Reader-python) 556 | * [Ruby](https://github.com/maxmind/MaxMind-DB-Reader-ruby) 557 | 558 | ## Authors 559 | 560 | This specification was created by the following authors: 561 | 562 | * Greg Oschwald \ 563 | * Dave Rolsky \ 564 | * Boris Zentner \ 565 | 566 | ## License 567 | 568 | This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 569 | Unported License. To view a copy of this license, visit 570 | [http://creativecommons.org/licenses/by-sa/3.0/](http://creativecommons.org/licenses/by-sa/3.0/) 571 | or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain 572 | View, California, 94041, USA 573 | 574 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MaxMind DB is a binary file format that stores data indexed by IP address 2 | subnets (IPv4 or IPv6). 3 | 4 | This repository contains the spec for that format as well as test databases. 5 | 6 | # Copyright and License 7 | 8 | This software is Copyright (c) 2013 - 2025 by MaxMind, Inc. 9 | 10 | This is free software, licensed under the [Apache License, Version 11 | 2.0](LICENSE-APACHE) or the [MIT License](LICENSE-MIT), at your option. 12 | -------------------------------------------------------------------------------- /bad-data/README.md: -------------------------------------------------------------------------------- 1 | These are corrupt databases that have been know to cause problems such as 2 | segfaults or unhandled errors on one or more MaxMind DB reader 3 | implementations. Implementations _should_ return an appropriate error 4 | or raise an exception on these databases. 5 | 6 | If you find a corrupt test-sized database that crashes a MMDB reader library, 7 | please feel free to add it here by creating a pull request. 8 | -------------------------------------------------------------------------------- /bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb -------------------------------------------------------------------------------- /bad-data/maxminddb-golang/cyclic-data-structure.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/maxminddb-golang/cyclic-data-structure.mmdb -------------------------------------------------------------------------------- /bad-data/maxminddb-golang/invalid-bytes-length.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/maxminddb-golang/invalid-bytes-length.mmdb -------------------------------------------------------------------------------- /bad-data/maxminddb-golang/invalid-data-record-offset.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb -------------------------------------------------------------------------------- /bad-data/maxminddb-golang/invalid-map-key-length.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/maxminddb-golang/invalid-map-key-length.mmdb -------------------------------------------------------------------------------- /bad-data/maxminddb-golang/invalid-string-length.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/maxminddb-golang/invalid-string-length.mmdb -------------------------------------------------------------------------------- /bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb -------------------------------------------------------------------------------- /bad-data/maxminddb-golang/unexpected-bytes.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/maxminddb-golang/unexpected-bytes.mmdb -------------------------------------------------------------------------------- /bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb -------------------------------------------------------------------------------- /cmd/write-test-data/main.go: -------------------------------------------------------------------------------- 1 | // write-test-data generates test mmdb files. 2 | package main 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/maxmind/MaxMind-DB/pkg/writer" 10 | ) 11 | 12 | func main() { 13 | source := flag.String("source", "", "Source data directory") 14 | target := flag.String("target", "", "Destination directory for the generated mmdb files") 15 | 16 | flag.Parse() 17 | 18 | w, err := writer.New(*source, *target) 19 | if err != nil { 20 | fmt.Printf("creating writer: %+v\n", err) 21 | os.Exit(1) 22 | } 23 | 24 | if err := w.WriteIPv4TestDB(); err != nil { 25 | fmt.Printf("writing IPv4 test databases: %+v\n", err) 26 | os.Exit(1) 27 | } 28 | 29 | if err := w.WriteIPv6TestDB(); err != nil { 30 | fmt.Printf("writing IPv6 test databases: %+v\n", err) 31 | os.Exit(1) 32 | } 33 | 34 | if err := w.WriteMixedIPTestDB(); err != nil { 35 | fmt.Printf("writing IPv6 test databases: %+v\n", err) 36 | os.Exit(1) 37 | } 38 | 39 | if err := w.WriteNoIPv4TestDB(); err != nil { 40 | fmt.Printf("writing no IPv4 test databases: %+v\n", err) 41 | os.Exit(1) 42 | } 43 | 44 | if err := w.WriteNoMapTestDB(); err != nil { 45 | fmt.Printf("writing no map test databases: %+v\n", err) 46 | os.Exit(1) 47 | } 48 | 49 | if err := w.WriteMetadataPointersTestDB(); err != nil { 50 | fmt.Printf("writing metadata pointers test databases: %+v\n", err) 51 | os.Exit(1) 52 | } 53 | 54 | if err := w.WriteDecoderTestDB(); err != nil { 55 | fmt.Printf("writing decoder test databases: %+v\n", err) 56 | os.Exit(1) 57 | } 58 | 59 | if err := w.WriteDeeplyNestedStructuresTestDB(); err != nil { 60 | fmt.Printf("writing decoder test databases: %+v\n", err) 61 | os.Exit(1) 62 | } 63 | 64 | if err := w.WriteGeoIP2TestDB(); err != nil { 65 | fmt.Printf("writing GeoIP2 test databases: %+v\n", err) 66 | os.Exit(1) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maxmind/MaxMind-DB 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/maxmind/mmdbwriter v1.0.0 7 | go4.org/netipx v0.0.0-20230824141953-6213f710f925 8 | ) 9 | 10 | require ( 11 | github.com/oschwald/maxminddb-golang v1.12.0 // indirect 12 | golang.org/x/sys v0.10.0 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/maxmind/mmdbwriter v1.0.0 h1:bieL4P6yaYaHvbtLSwnKtEvScUKKD6jcKaLiTM3WSMw= 4 | github.com/maxmind/mmdbwriter v1.0.0/go.mod h1:noBMCUtyN5PUQ4H8ikkOvGSHhzhLok51fON2hcrpKj8= 5 | github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= 6 | github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 10 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 11 | go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ= 12 | go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 13 | golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= 14 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 15 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 16 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | -------------------------------------------------------------------------------- /pkg/writer/decoder.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "math" 8 | "math/big" 9 | "net/netip" 10 | 11 | "github.com/maxmind/mmdbwriter" 12 | "github.com/maxmind/mmdbwriter/mmdbtype" 13 | "go4.org/netipx" 14 | ) 15 | 16 | // WriteDecoderTestDB writes an mmdb file with all possible record value types. 17 | func (w *Writer) WriteDecoderTestDB() error { 18 | dbWriter, err := mmdbwriter.New( 19 | mmdbwriter.Options{ 20 | DatabaseType: "MaxMind DB Decoder Test", 21 | Description: map[string]string{ 22 | "en": "MaxMind DB Decoder Test database - contains every MaxMind DB data type", 23 | }, 24 | DisableIPv4Aliasing: false, 25 | IncludeReservedNetworks: true, 26 | IPVersion: 6, 27 | Languages: []string{"en"}, 28 | RecordSize: 24, 29 | }, 30 | ) 31 | if err != nil { 32 | return fmt.Errorf("creating mmdbwriter: %w", err) 33 | } 34 | 35 | addrs, err := parseIPSlice(ipSample) 36 | if err != nil { 37 | return fmt.Errorf("parsing ip addresses: %w", err) 38 | } 39 | if err := insertAllTypes(dbWriter, addrs); err != nil { 40 | return fmt.Errorf("inserting all types records: %w", err) 41 | } 42 | 43 | zeroAddr, err := netip.ParsePrefix("::0.0.0.0/128") 44 | if err != nil { 45 | return fmt.Errorf("parsing ip: %w", err) 46 | } 47 | if err := insertAllTypesZero(dbWriter, []netip.Prefix{zeroAddr}); err != nil { 48 | return fmt.Errorf("inserting all types records: %w", err) 49 | } 50 | 51 | maxAddr, err := netip.ParsePrefix("::255.255.255.255/128") 52 | if err != nil { 53 | return fmt.Errorf("parsing ip: %w", err) 54 | } 55 | if err := insertNumericMax(dbWriter, []netip.Prefix{maxAddr}); err != nil { 56 | return fmt.Errorf("inserting all types records: %w", err) 57 | } 58 | 59 | if err := w.write(dbWriter, "MaxMind-DB-test-decoder.mmdb"); err != nil { 60 | return fmt.Errorf("writing database: %w", err) 61 | } 62 | return nil 63 | } 64 | 65 | // insertAllTypes inserts records with all possible value types. 66 | func insertAllTypes(w *mmdbwriter.Tree, ipAddresses []netip.Prefix) error { 67 | buf := new(bytes.Buffer) 68 | if err := binary.Write(buf, binary.BigEndian, uint32(42)); err != nil { 69 | return fmt.Errorf("creating buffer for all types record: %w", err) 70 | } 71 | 72 | ui64 := big.Int{} 73 | ui64.Lsh(big.NewInt(1), 60) 74 | 75 | ui128 := big.Int{} 76 | ui128.Lsh(big.NewInt(1), 120) 77 | mmdbUint128 := mmdbtype.Uint128(ui128) 78 | 79 | allTypes := mmdbtype.Map{ 80 | "array": mmdbtype.Slice{ 81 | mmdbtype.Uint32(1), 82 | mmdbtype.Uint32(2), 83 | mmdbtype.Uint32(3), 84 | }, 85 | "bytes": mmdbtype.Bytes(buf.Bytes()), 86 | "boolean": mmdbtype.Bool(true), 87 | "double": mmdbtype.Float64(42.123456), 88 | "float": mmdbtype.Float32(1.1), 89 | "int32": mmdbtype.Int32(-1 * math.Pow(2, 28)), 90 | "map": mmdbtype.Map{ 91 | "mapX": mmdbtype.Map{ 92 | "utf8_stringX": mmdbtype.String("hello"), 93 | "arrayX": mmdbtype.Slice{ 94 | mmdbtype.Uint32(7), 95 | mmdbtype.Uint32(8), 96 | mmdbtype.Uint32(9), 97 | }, 98 | }, 99 | }, 100 | "uint16": mmdbtype.Uint16(100), 101 | "uint32": mmdbtype.Uint32(math.Pow(2, 28)), 102 | "uint64": mmdbtype.Uint64(ui64.Uint64()), 103 | "uint128": mmdbUint128.Copy(), 104 | "utf8_string": mmdbtype.String("unicode! ☯ - ♫"), 105 | } 106 | 107 | for _, addr := range ipAddresses { 108 | err := w.Insert( 109 | netipx.PrefixIPNet(addr), 110 | allTypes, 111 | ) 112 | if err != nil { 113 | return fmt.Errorf("inserting ip: %w", err) 114 | } 115 | } 116 | return nil 117 | } 118 | 119 | // insertAllTypesZero inserts records with all possible value types with zero values. 120 | func insertAllTypesZero(w *mmdbwriter.Tree, ipAddresses []netip.Prefix) error { 121 | var uint128 big.Int 122 | mmdbUint128 := mmdbtype.Uint128(uint128) 123 | 124 | zeroValues := mmdbtype.Map{ 125 | "array": mmdbtype.Slice{}, 126 | "bytes": mmdbtype.Bytes([]byte{}), 127 | "boolean": mmdbtype.Bool(false), 128 | "double": mmdbtype.Float64(0), 129 | "float": mmdbtype.Float32(0), 130 | "int32": mmdbtype.Int32(0), 131 | "map": mmdbtype.Map{}, 132 | "uint16": mmdbtype.Uint16(0), 133 | "uint32": mmdbtype.Uint32(0), 134 | "uint64": mmdbtype.Uint64(0), 135 | "uint128": mmdbUint128.Copy(), 136 | "utf8_string": mmdbtype.String(""), 137 | } 138 | 139 | for _, addr := range ipAddresses { 140 | err := w.Insert( 141 | netipx.PrefixIPNet(addr), 142 | zeroValues, 143 | ) 144 | if err != nil { 145 | return fmt.Errorf("inserting ip: %w", err) 146 | } 147 | } 148 | return nil 149 | } 150 | 151 | // insertNumericMax inserts records with numeric types maxed out. 152 | func insertNumericMax(w *mmdbwriter.Tree, ipAddresses []netip.Prefix) error { 153 | var uint128Max big.Int 154 | uint128Max.Exp(big.NewInt(2), big.NewInt(128), nil) 155 | uint128Max.Sub(&uint128Max, big.NewInt(1)) 156 | mmdbUint128 := mmdbtype.Uint128(uint128Max) 157 | 158 | numMax := mmdbtype.Map{ 159 | "double": mmdbtype.Float64(math.Inf(1)), 160 | "float": mmdbtype.Float32(float32(math.Inf(1))), 161 | "int32": mmdbtype.Int32(1<<31 - 1), 162 | "uint16": mmdbtype.Uint16(0xffff), 163 | "uint32": mmdbtype.Uint32(0xffffffff), 164 | "uint64": mmdbtype.Uint64(0xffffffffffffffff), 165 | "uint128": mmdbUint128.Copy(), 166 | } 167 | 168 | for _, addr := range ipAddresses { 169 | err := w.Insert( 170 | netipx.PrefixIPNet(addr), 171 | numMax, 172 | ) 173 | if err != nil { 174 | return fmt.Errorf("inserting ip: %w", err) 175 | } 176 | } 177 | return nil 178 | } 179 | -------------------------------------------------------------------------------- /pkg/writer/geoip2.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/netip" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/maxmind/mmdbwriter" 12 | "github.com/maxmind/mmdbwriter/mmdbtype" 13 | "go4.org/netipx" 14 | ) 15 | 16 | // WriteGeoIP2TestDB writes GeoIP2 test mmdb files. 17 | func (w *Writer) WriteGeoIP2TestDB() error { 18 | // This is a map from database type to input file name. 19 | dbTypes := map[string]string{ 20 | "GeoIP-Anonymous-Plus": "GeoIP-Anonymous-Plus-Test.json", 21 | "GeoIP2-Anonymous-IP": "GeoIP2-Anonymous-IP-Test.json", 22 | "GeoIP2-City": "GeoIP2-City-Test.json", 23 | "GeoIP2-City-Shield": "GeoIP2-City-Test.json", 24 | "GeoIP2-Connection-Type": "GeoIP2-Connection-Type-Test.json", 25 | "GeoIP2-Country": "GeoIP2-Country-Test.json", 26 | "GeoIP2-Country-Shield": "GeoIP2-Country-Test.json", 27 | "GeoIP2-DensityIncome": "GeoIP2-DensityIncome-Test.json", 28 | "GeoIP2-Domain": "GeoIP2-Domain-Test.json", 29 | "GeoIP2-Enterprise": "GeoIP2-Enterprise-Test.json", 30 | "GeoIP2-Enterprise-Shield": "GeoIP2-Enterprise-Test.json", 31 | "GeoIP2-IP-Risk": "GeoIP2-IP-Risk-Test.json", 32 | "GeoIP2-ISP": "GeoIP2-ISP-Test.json", 33 | "GeoIP2-Precision-Enterprise": "GeoIP2-Precision-Enterprise-Test.json", 34 | "GeoIP2-Precision-Enterprise-Shield": "GeoIP2-Precision-Enterprise-Test.json", 35 | "GeoIP2-Static-IP-Score": "GeoIP2-Static-IP-Score-Test.json", 36 | "GeoIP2-User-Count": "GeoIP2-User-Count-Test.json", 37 | "GeoLite2-ASN": "GeoLite2-ASN-Test.json", 38 | "GeoLite2-City": "GeoLite2-City-Test.json", 39 | "GeoLite2-Country": "GeoLite2-Country-Test.json", 40 | } 41 | 42 | for dbType, jsonFileName := range dbTypes { 43 | languages := []string{"en"} 44 | description := map[string]string{ 45 | "en": strings.ReplaceAll(dbType, "-", " ") + 46 | " Test Database (fake GeoIP2 data, for example purposes only)", 47 | } 48 | 49 | if dbType == "GeoIP2-City" { 50 | languages = append(languages, "zh") 51 | description["zh"] = "小型数据库" 52 | } 53 | 54 | dbWriter, err := mmdbwriter.New( 55 | mmdbwriter.Options{ 56 | DatabaseType: dbType, 57 | Description: description, 58 | DisableIPv4Aliasing: false, 59 | IPVersion: 6, 60 | Languages: languages, 61 | RecordSize: 28, 62 | }, 63 | ) 64 | if err != nil { 65 | return fmt.Errorf("creating mmdbwriter: %w", err) 66 | } 67 | 68 | if dbType == "GeoIP2-Anonymous-IP" || dbType == "GeoIP-Anonymous-Plus" { 69 | if err := populateAllNetworks(dbWriter); err != nil { 70 | return fmt.Errorf("inserting all networks: %w", err) 71 | } 72 | } 73 | 74 | if err := InsertJSON(dbWriter, filepath.Join(w.source, jsonFileName)); err != nil { 75 | return fmt.Errorf("inserting json: %w", err) 76 | } 77 | 78 | dbFileName := dbType + "-Test.mmdb" 79 | if err := w.write(dbWriter, dbFileName); err != nil { 80 | return fmt.Errorf("writing database: %w", err) 81 | } 82 | } 83 | 84 | return nil 85 | } 86 | 87 | // InsertJSON reads and parses a json file into mmdbtypes values and inserts 88 | // them into the mmdbwriter tree. 89 | func InsertJSON(dbWriter *mmdbwriter.Tree, filePath string) error { 90 | file, err := os.Open(filepath.Clean(filePath)) 91 | if err != nil { 92 | return fmt.Errorf("opening json file: %w", err) 93 | } 94 | defer file.Close() 95 | 96 | var data []map[string]any 97 | if err := json.NewDecoder(file).Decode(&data); err != nil { 98 | return fmt.Errorf("decoding json file: %w", err) 99 | } 100 | 101 | for _, record := range data { 102 | for k, v := range record { 103 | prefix, err := netip.ParsePrefix(k) 104 | if err != nil { 105 | return fmt.Errorf("parsing ip: %w", err) 106 | } 107 | 108 | mmdbValue, err := toMMDBType(prefix.String(), v) 109 | if err != nil { 110 | return fmt.Errorf("converting value to mmdbtype: %w", err) 111 | } 112 | 113 | err = dbWriter.Insert( 114 | netipx.PrefixIPNet(prefix), 115 | mmdbValue, 116 | ) 117 | if err != nil { 118 | return fmt.Errorf("inserting ip: %w", err) 119 | } 120 | } 121 | } 122 | return nil 123 | } 124 | 125 | // toMMDBType key converts field values read from json into their corresponding mmdbtype.DataType. 126 | // It makes some assumptions for numeric types based on previous knowledge about field types. 127 | func toMMDBType(key string, value any) (mmdbtype.DataType, error) { 128 | switch v := value.(type) { 129 | case bool: 130 | return mmdbtype.Bool(v), nil 131 | case string: 132 | return mmdbtype.String(v), nil 133 | case map[string]any: 134 | m := mmdbtype.Map{} 135 | for innerKey, val := range v { 136 | innerVal, err := toMMDBType(innerKey, val) 137 | if err != nil { 138 | return nil, fmt.Errorf("parsing mmdbtype.Map for key %q: %w", key, err) 139 | } 140 | m[mmdbtype.String(innerKey)] = innerVal 141 | } 142 | return m, nil 143 | case []any: 144 | s := mmdbtype.Slice{} 145 | for _, val := range v { 146 | innerVal, err := toMMDBType(key, val) 147 | if err != nil { 148 | return nil, fmt.Errorf("parsing mmdbtype.Slice for key %q: %w", key, err) 149 | } 150 | s = append(s, innerVal) 151 | } 152 | return s, nil 153 | case float64: 154 | switch key { 155 | case "accuracy_radius", "anonymizer_confidence", "confidence", "metro_code": 156 | return mmdbtype.Uint16(v), nil 157 | case "autonomous_system_number", "average_income", 158 | "geoname_id", "ipv4_24", "ipv4_32", "ipv6_32", 159 | "ipv6_48", "ipv6_64", "population_density": 160 | return mmdbtype.Uint32(v), nil 161 | case "ip_risk", "latitude", "longitude", "score", 162 | "static_ip_score": 163 | return mmdbtype.Float64(v), nil 164 | default: 165 | return nil, fmt.Errorf("unsupported numeric type for key %q: %T", key, value) 166 | } 167 | default: 168 | return nil, fmt.Errorf("unsupported type for key %q: %T", key, value) 169 | } 170 | } 171 | 172 | // populate all networks inserts all networks into the writer with an empty map value. 173 | func populateAllNetworks(w *mmdbwriter.Tree) error { 174 | defaultNet, err := netip.ParsePrefix("::/0") 175 | if err != nil { 176 | return fmt.Errorf("parsing ip: %w", err) 177 | } 178 | 179 | err = w.Insert( 180 | netipx.PrefixIPNet(defaultNet), 181 | mmdbtype.Map{}, 182 | ) 183 | if err != nil { 184 | return fmt.Errorf("inserting ip: %w", err) 185 | } 186 | 187 | return nil 188 | } 189 | -------------------------------------------------------------------------------- /pkg/writer/ip.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "fmt" 5 | "net/netip" 6 | 7 | "go4.org/netipx" 8 | ) 9 | 10 | // parseIPRange takes IP addresses in string presentation form that represent a 11 | // range and returns an IP range. 12 | func parseIPRange(from, to string) (netipx.IPRange, error) { 13 | startIP, err := netip.ParseAddr(from) 14 | if err != nil { 15 | return netipx.IPRange{}, fmt.Errorf("parsing %s as an IP: %w", from, err) 16 | } 17 | endIP, err := netip.ParseAddr(to) 18 | if err != nil { 19 | return netipx.IPRange{}, fmt.Errorf("parsing %s as an IP: %w", to, err) 20 | } 21 | ipRange := netipx.IPRangeFrom(startIP, endIP) 22 | if !ipRange.IsValid() { 23 | return netipx.IPRange{}, fmt.Errorf("%s-%s is an invalid IP range", startIP, endIP) 24 | } 25 | return ipRange, nil 26 | } 27 | 28 | // parseIPSlice parses a slice of IP address strings and returns a slice of netip.Prefix. 29 | func parseIPSlice(ipAddresses []string) ([]netip.Prefix, error) { 30 | var addrs []netip.Prefix 31 | for _, ip := range ipAddresses { 32 | addr, err := netip.ParsePrefix(ip) 33 | if err != nil { 34 | return nil, fmt.Errorf("parsing %s as an IP: %w", ip, err) 35 | } 36 | addrs = append(addrs, addr) 37 | } 38 | return addrs, nil 39 | } 40 | -------------------------------------------------------------------------------- /pkg/writer/maxmind.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "fmt" 5 | "net/netip" 6 | 7 | "github.com/maxmind/mmdbwriter" 8 | "github.com/maxmind/mmdbwriter/mmdbtype" 9 | "go4.org/netipx" 10 | ) 11 | 12 | // WriteIPv4TestDB writes mmdb files for an ip range between 1.1.1.1 and 1.1.1.32 13 | // with various record sizes. 14 | func (w *Writer) WriteIPv4TestDB() error { 15 | ipRange, err := parseIPRange("1.1.1.1", "1.1.1.32") 16 | if err != nil { 17 | return fmt.Errorf("parsing ip range: %w", err) 18 | } 19 | 20 | for _, recordSize := range []int{24, 28, 32} { 21 | err := w.writeMaxMindTestDB( 22 | recordSize, 23 | []netipx.IPRange{ipRange}, 24 | "ipv4", 25 | ) 26 | if err != nil { 27 | return fmt.Errorf("writing test database: %w", err) 28 | } 29 | } 30 | 31 | return nil 32 | } 33 | 34 | // WriteIPv6TestDB writes mmdb files for an ip range between ::1:ffff:ffff and ::2:0000:0059 35 | // with various record sizes. 36 | func (w *Writer) WriteIPv6TestDB() error { 37 | ipRange, err := parseIPRange("::1:ffff:ffff", "::2:0000:0059") 38 | if err != nil { 39 | return fmt.Errorf("parsing ip range: %w", err) 40 | } 41 | 42 | for _, recordSize := range []int{24, 28, 32} { 43 | err := w.writeMaxMindTestDB( 44 | recordSize, 45 | []netipx.IPRange{ipRange}, 46 | "ipv6", 47 | ) 48 | if err != nil { 49 | return fmt.Errorf("writing test database: %w", err) 50 | } 51 | } 52 | 53 | return nil 54 | } 55 | 56 | // WriteMixedIPTestDB writes mmdb files for a mixed ip version range between ::1:ffff:ffff and 57 | // ::2:0000:0059 58 | // with various record sizes. 59 | func (w *Writer) WriteMixedIPTestDB() error { 60 | ipv6Range, err := parseIPRange("::1:ffff:ffff", "::2:0000:0059") 61 | if err != nil { 62 | return fmt.Errorf("parsing ip range: %w", err) 63 | } 64 | 65 | ipv4Range, err := parseIPRange("1.1.1.1", "1.1.1.32") 66 | if err != nil { 67 | return fmt.Errorf("parsing ip range: %w", err) 68 | } 69 | 70 | for _, recordSize := range []int{24, 28, 32} { 71 | err := w.writeMaxMindTestDB( 72 | recordSize, 73 | []netipx.IPRange{ipv6Range, ipv4Range}, 74 | "mixed", 75 | ) 76 | if err != nil { 77 | return fmt.Errorf("writing test database: %w", err) 78 | } 79 | } 80 | 81 | return nil 82 | } 83 | 84 | // writeMaxMindTestDB writes test mmdb files. 85 | func (w *Writer) writeMaxMindTestDB( 86 | recordSize int, 87 | ipRange []netipx.IPRange, 88 | ipVersionName string, 89 | ) error { 90 | ipVersion := 6 91 | if ipRange[0].From().Is4() { 92 | ipVersion = 4 93 | } 94 | 95 | metadata := map[string]string{} 96 | metadata["en"] = "Test Database" 97 | metadata["zh"] = "Test Database Chinese" 98 | 99 | dbWriter, err := mmdbwriter.New( 100 | mmdbwriter.Options{ 101 | DatabaseType: "Test", 102 | Description: metadata, 103 | DisableIPv4Aliasing: ipVersion == 4, 104 | IPVersion: ipVersion, 105 | Languages: []string{"en", "zh"}, 106 | RecordSize: recordSize, 107 | }, 108 | ) 109 | if err != nil { 110 | return fmt.Errorf("creating mmdbwriter: %w", err) 111 | } 112 | 113 | for _, ir := range ipRange { 114 | for _, prefix := range ir.Prefixes() { 115 | ipString := prefix.Addr().String() 116 | if ipVersion == 6 && prefix.Addr().Is4() { 117 | ipString = "::" + ipString 118 | } 119 | 120 | err := dbWriter.Insert( 121 | netipx.PrefixIPNet(prefix), 122 | mmdbtype.Map{ 123 | "ip": mmdbtype.String(ipString), 124 | }, 125 | ) 126 | if err != nil { 127 | return fmt.Errorf("inserting ip: %w", err) 128 | } 129 | } 130 | } 131 | 132 | fileName := fmt.Sprintf("MaxMind-DB-test-%s-%d.mmdb", ipVersionName, recordSize) 133 | if err := w.write(dbWriter, fileName); err != nil { 134 | return fmt.Errorf("writing database: %w", err) 135 | } 136 | 137 | return nil 138 | } 139 | 140 | // WriteNoIPv4TestDB writes an mmdb file with no ipv4 records. 141 | func (w *Writer) WriteNoIPv4TestDB() error { 142 | dbWriter, err := mmdbwriter.New( 143 | mmdbwriter.Options{ 144 | DatabaseType: "MaxMind DB No IPv4 Search Tree", 145 | Description: map[string]string{ 146 | "en": "MaxMind DB No IPv4 Search Tree", 147 | }, 148 | DisableIPv4Aliasing: true, 149 | IncludeReservedNetworks: true, 150 | IPVersion: 6, 151 | Languages: []string{"en"}, 152 | RecordSize: 24, 153 | }, 154 | ) 155 | if err != nil { 156 | return fmt.Errorf("creating mmdbwriter: %w", err) 157 | } 158 | 159 | addr, err := netip.ParsePrefix("::/64") 160 | if err != nil { 161 | return fmt.Errorf("parsing ip: %w", err) 162 | } 163 | 164 | err = dbWriter.Insert( 165 | netipx.PrefixIPNet(addr), 166 | mmdbtype.String(addr.String()), 167 | ) 168 | if err != nil { 169 | return fmt.Errorf("inserting ip: %w", err) 170 | } 171 | 172 | if err := w.write(dbWriter, "MaxMind-DB-no-ipv4-search-tree.mmdb"); err != nil { 173 | return fmt.Errorf("writing database: %w", err) 174 | } 175 | return nil 176 | } 177 | 178 | // WriteNoMapTestDB writes an mmdb file where each record points to 179 | // a string value. 180 | func (w *Writer) WriteNoMapTestDB() error { 181 | dbWriter, err := mmdbwriter.New( 182 | mmdbwriter.Options{ 183 | DatabaseType: "MaxMind DB String Value Entries", 184 | Description: map[string]string{ 185 | "en": "MaxMind DB String Value Entries (no maps or arrays as values)", 186 | }, 187 | IPVersion: 4, 188 | Languages: []string{"en"}, 189 | RecordSize: 24, 190 | }, 191 | ) 192 | if err != nil { 193 | return fmt.Errorf("creating mmdbwriter: %w", err) 194 | } 195 | 196 | ipRange, err := parseIPRange("1.1.1.1", "1.1.1.32") 197 | if err != nil { 198 | return fmt.Errorf("parsing ip range: %w", err) 199 | } 200 | 201 | for _, prefix := range ipRange.Prefixes() { 202 | err := dbWriter.Insert( 203 | netipx.PrefixIPNet(prefix), 204 | mmdbtype.String(prefix.String()), 205 | ) 206 | if err != nil { 207 | return fmt.Errorf("inserting ip: %w", err) 208 | } 209 | } 210 | 211 | if err := w.write(dbWriter, "MaxMind-DB-string-value-entries.mmdb"); err != nil { 212 | return fmt.Errorf("writing database: %w", err) 213 | } 214 | return nil 215 | } 216 | 217 | // WriteMetadataPointersTestDB writes an mmdb file with metadata pointers allowed. 218 | func (w *Writer) WriteMetadataPointersTestDB() error { 219 | repeatedString := "Lots of pointers in metadata" 220 | dbWriter, err := mmdbwriter.New( 221 | mmdbwriter.Options{ 222 | DatabaseType: repeatedString, 223 | Description: map[string]string{ 224 | "en": repeatedString, 225 | "es": repeatedString, 226 | "zh": repeatedString, 227 | }, 228 | DisableIPv4Aliasing: true, 229 | IPVersion: 6, 230 | Languages: []string{"en", "es", "zh"}, 231 | RecordSize: 24, 232 | }, 233 | ) 234 | if err != nil { 235 | return fmt.Errorf("creating mmdbwriter: %w", err) 236 | } 237 | 238 | if err := populateAllNetworks(dbWriter); err != nil { 239 | return fmt.Errorf("inserting all networks: %w", err) 240 | } 241 | 242 | if err := w.write(dbWriter, "MaxMind-DB-test-metadata-pointers.mmdb"); err != nil { 243 | return fmt.Errorf("writing database: %w", err) 244 | } 245 | return nil 246 | } 247 | -------------------------------------------------------------------------------- /pkg/writer/nestedstructures.go: -------------------------------------------------------------------------------- 1 | package writer 2 | 3 | import ( 4 | "fmt" 5 | "net/netip" 6 | 7 | "github.com/maxmind/mmdbwriter" 8 | "github.com/maxmind/mmdbwriter/mmdbtype" 9 | "go4.org/netipx" 10 | ) 11 | 12 | // WriteDeeplyNestedStructuresTestDB writes an mmdb file with deeply nested record value types. 13 | func (w *Writer) WriteDeeplyNestedStructuresTestDB() error { 14 | dbWriter, err := mmdbwriter.New( 15 | mmdbwriter.Options{ 16 | DatabaseType: "MaxMind DB Nested Data Structures", 17 | Description: map[string]string{ 18 | "en": "MaxMind DB Nested Data Structures Test database - contains deeply nested map/array structures", 19 | }, 20 | DisableIPv4Aliasing: false, 21 | IncludeReservedNetworks: true, 22 | IPVersion: 6, 23 | Languages: []string{"en"}, 24 | RecordSize: 24, 25 | }, 26 | ) 27 | if err != nil { 28 | return fmt.Errorf("creating mmdbwriter: %w", err) 29 | } 30 | 31 | addrs, err := parseIPSlice(ipSample) 32 | if err != nil { 33 | return fmt.Errorf("parsing ip addresses: %w", err) 34 | } 35 | if err := insertNestedStructure(dbWriter, addrs); err != nil { 36 | return fmt.Errorf("inserting all types records: %w", err) 37 | } 38 | 39 | if err := w.write(dbWriter, "MaxMind-DB-test-nested.mmdb"); err != nil { 40 | return fmt.Errorf("writing database: %w", err) 41 | } 42 | return nil 43 | } 44 | 45 | // insertNestedStructure inserts records with deeply nested structures. 46 | func insertNestedStructure(w *mmdbwriter.Tree, ipAddresses []netip.Prefix) error { 47 | nestedStruct := mmdbtype.Map{ 48 | "map1": mmdbtype.Map{ 49 | "map2": mmdbtype.Map{ 50 | "array": mmdbtype.Slice{ 51 | mmdbtype.Map{ 52 | "map3": mmdbtype.Map{ 53 | "a": mmdbtype.Uint32(1), 54 | "b": mmdbtype.Uint32(2), 55 | "c": mmdbtype.Uint32(3), 56 | }, 57 | }, 58 | }, 59 | }, 60 | }, 61 | } 62 | 63 | for _, addr := range ipAddresses { 64 | err := w.Insert( 65 | netipx.PrefixIPNet(addr), 66 | nestedStruct, 67 | ) 68 | if err != nil { 69 | return fmt.Errorf("inserting ip: %w", err) 70 | } 71 | } 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /pkg/writer/writer.go: -------------------------------------------------------------------------------- 1 | // Package writer defines database writers responsible 2 | // for generating test mmdb files. 3 | package writer 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "io/fs" 9 | "os" 10 | "path/filepath" 11 | 12 | "github.com/maxmind/mmdbwriter" 13 | ) 14 | 15 | var ipSample = []string{ 16 | "::1.1.1.0/120", 17 | "::2.2.0.0/112", 18 | "::3.0.0.0/104", 19 | "::4.5.6.7/128", 20 | "abcd::/64", 21 | "1000::1234:0000/112", 22 | } 23 | 24 | // Writer is responsible for writing test mmdb databases 25 | // based on the provided data sources. 26 | type Writer struct { 27 | source string 28 | target string 29 | } 30 | 31 | // New initializes a new test database writer struct. 32 | func New(source, target string) (*Writer, error) { 33 | s := filepath.Clean(source) 34 | if _, err := os.Stat(s); errors.Is(err, fs.ErrNotExist) { 35 | return nil, fmt.Errorf("source directory does not exist: %w", err) 36 | } 37 | 38 | t := filepath.Clean(target) 39 | //nolint:gosec // not security sensitive. 40 | if err := os.MkdirAll(t, os.ModePerm); err != nil { 41 | return nil, fmt.Errorf("creating target directory: %w", err) 42 | } 43 | 44 | return &Writer{ 45 | source: s, 46 | target: t, 47 | }, nil 48 | } 49 | 50 | func (w *Writer) write(dbWriter *mmdbwriter.Tree, fileName string) error { 51 | outputFile, err := os.Create(filepath.Clean(filepath.Join(w.target, fileName))) 52 | if err != nil { 53 | return fmt.Errorf("creating mmdb file: %w", err) 54 | } 55 | defer outputFile.Close() 56 | 57 | if _, err := dbWriter.WriteTo(outputFile); err != nil { 58 | return fmt.Errorf("writing mmdb file: %w", err) 59 | } 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /source-data/GeoIP-Anonymous-Plus-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "1.2.0.0/32": { 4 | "is_anonymous": true, 5 | "is_anonymous_vpn": true 6 | } 7 | }, 8 | { 9 | "1.2.0.1/32": { 10 | "anonymizer_confidence": 30, 11 | "is_anonymous": true, 12 | "is_anonymous_vpn": true, 13 | "network_last_seen": "2025-04-14", 14 | "provider_name": "foo" 15 | } 16 | }, 17 | { 18 | "1.2.0.2/32": { 19 | "anonymizer_confidence": 99, 20 | "is_anonymous": true, 21 | "is_anonymous_vpn": true 22 | } 23 | }, 24 | { 25 | "1.2.0.3/32": { 26 | "is_anonymous": true, 27 | "is_anonymous_vpn": true, 28 | "network_last_seen": "2025-04-14" 29 | } 30 | }, 31 | { 32 | "1.2.0.4/32": { 33 | "is_anonymous": true, 34 | "is_anonymous_vpn": true, 35 | "provider_name": "foo" 36 | } 37 | }, 38 | { 39 | "1.2.0.5/32": { 40 | "is_anonymous": true, 41 | "is_anonymous_vpn": true 42 | } 43 | }, 44 | { 45 | "1.2.0.6/31": { 46 | "is_anonymous": true, 47 | "is_anonymous_vpn": true 48 | } 49 | }, 50 | { 51 | "1.2.0.8/29": { 52 | "is_anonymous": true, 53 | "is_anonymous_vpn": true 54 | } 55 | }, 56 | { 57 | "1.2.0.16/28": { 58 | "is_anonymous": true, 59 | "is_anonymous_vpn": true 60 | } 61 | }, 62 | { 63 | "1.2.0.32/27": { 64 | "is_anonymous": true, 65 | "is_anonymous_vpn": true 66 | } 67 | }, 68 | { 69 | "1.2.0.64/26": { 70 | "is_anonymous": true, 71 | "is_anonymous_vpn": true 72 | } 73 | }, 74 | { 75 | "1.2.0.128/25": { 76 | "is_anonymous": true, 77 | "is_anonymous_vpn": true 78 | } 79 | }, 80 | { 81 | "1.2.1.0/24": { 82 | "is_anonymous": true, 83 | "is_anonymous_vpn": true 84 | } 85 | }, 86 | { 87 | "1.2.2.0/23": { 88 | "is_anonymous": true, 89 | "is_anonymous_vpn": true 90 | } 91 | }, 92 | { 93 | "1.2.4.0/22": { 94 | "is_anonymous": true, 95 | "is_anonymous_vpn": true 96 | } 97 | }, 98 | { 99 | "1.2.8.0/21": { 100 | "is_anonymous": true, 101 | "is_anonymous_vpn": true 102 | } 103 | }, 104 | { 105 | "1.2.16.0/20": { 106 | "is_anonymous": true, 107 | "is_anonymous_vpn": true 108 | } 109 | }, 110 | { 111 | "1.2.32.0/19": { 112 | "is_anonymous": true, 113 | "is_anonymous_vpn": true 114 | } 115 | }, 116 | { 117 | "1.2.64.0/18": { 118 | "is_anonymous": true, 119 | "is_anonymous_vpn": true 120 | } 121 | }, 122 | { 123 | "1.2.128.0/17": { 124 | "is_anonymous": true, 125 | "is_anonymous_vpn": true 126 | } 127 | }, 128 | { 129 | "::1.124.213.1/128": { 130 | "is_anonymous": true, 131 | "is_anonymous_vpn": true, 132 | "is_tor_exit_node": true 133 | } 134 | }, 135 | { 136 | "::81.2.69.0/120": { 137 | "is_anonymous": true, 138 | "is_anonymous_vpn": true, 139 | "is_hosting_provider": true, 140 | "is_public_proxy": true, 141 | "is_residential_proxy": true, 142 | "is_tor_exit_node": true 143 | } 144 | }, 145 | { 146 | "::71.160.223.0/120": { 147 | "is_anonymous": true, 148 | "is_hosting_provider": true 149 | } 150 | }, 151 | { 152 | "::186.30.236.0/120": { 153 | "is_anonymous": true, 154 | "is_public_proxy": true 155 | } 156 | }, 157 | { 158 | "::65.0.0.0/109": { 159 | "is_anonymous": true, 160 | "is_tor_exit_node": true 161 | } 162 | }, 163 | { 164 | "abcd:1000::/112": { 165 | "is_anonymous": true, 166 | "is_public_proxy": true 167 | } 168 | }, 169 | { 170 | "2001:480:3a::/64": { 171 | "is_anonymous": true, 172 | "is_public_proxy": true 173 | } 174 | } 175 | ] -------------------------------------------------------------------------------- /source-data/GeoIP2-Anonymous-IP-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "::1.2.0.0/112" : { 4 | "is_anonymous" : true, 5 | "is_anonymous_vpn" : true 6 | } 7 | }, 8 | { 9 | "::1.124.213.1/128" : { 10 | "is_anonymous" : true, 11 | "is_anonymous_vpn" : true, 12 | "is_tor_exit_node" : true 13 | } 14 | }, 15 | { 16 | "::81.2.69.0/120" : { 17 | "is_anonymous" : true, 18 | "is_anonymous_vpn" : true, 19 | "is_hosting_provider" : true, 20 | "is_public_proxy" : true, 21 | "is_residential_proxy" : true, 22 | "is_tor_exit_node" : true 23 | } 24 | }, 25 | { 26 | "::71.160.223.0/120" : { 27 | "is_anonymous" : true, 28 | "is_hosting_provider" : true 29 | } 30 | }, 31 | { 32 | "::186.30.236.0/120" : { 33 | "is_anonymous" : true, 34 | "is_public_proxy" : true 35 | } 36 | }, 37 | { 38 | "::65.0.0.0/109" : { 39 | "is_anonymous" : true, 40 | "is_tor_exit_node" : true 41 | } 42 | }, 43 | { 44 | "abcd:1000::/112" : { 45 | "is_anonymous" : true, 46 | "is_public_proxy" : true 47 | } 48 | }, 49 | { 50 | "2001:480:3a::/64" : { 51 | "is_anonymous" : true, 52 | "is_public_proxy" : true 53 | } 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /source-data/GeoIP2-Connection-Type-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "::1.0.0.0/120" : { 4 | "connection_type" : "Cable/DSL" 5 | } 6 | }, 7 | { 8 | "::1.0.1.0/120" : { 9 | "connection_type" : "Cellular" 10 | } 11 | }, 12 | { 13 | "::1.0.2.0/119" : { 14 | "connection_type" : "Cable/DSL" 15 | } 16 | }, 17 | { 18 | "::1.0.4.0/118" : { 19 | "connection_type" : "Cable/DSL" 20 | } 21 | }, 22 | { 23 | "::1.0.8.0/117" : { 24 | "connection_type" : "Cable/DSL" 25 | } 26 | }, 27 | { 28 | "::1.0.16.0/116" : { 29 | "connection_type" : "Cable/DSL" 30 | } 31 | }, 32 | { 33 | "::1.0.32.0/115" : { 34 | "connection_type" : "Cable/DSL" 35 | } 36 | }, 37 | { 38 | "::1.0.64.0/114" : { 39 | "connection_type" : "Cable/DSL" 40 | } 41 | }, 42 | { 43 | "::1.0.128.0/113" : { 44 | "connection_type" : "Cable/DSL" 45 | } 46 | }, 47 | { 48 | "::2.125.160.216/125" : { 49 | "connection_type" : "Cable/DSL" 50 | } 51 | }, 52 | { 53 | "::149.101.100.0/124" : { 54 | "connection_type" : "Cellular" 55 | } 56 | }, 57 | { 58 | "::67.43.156.0/120" : { 59 | "connection_type" : "Cellular" 60 | } 61 | }, 62 | { 63 | "::80.214.0.0/116" : { 64 | "connection_type" : "Cellular" 65 | } 66 | }, 67 | { 68 | "::96.1.0.0/112" : { 69 | "connection_type" : "Cable/DSL" 70 | } 71 | }, 72 | { 73 | "::96.10.0.0/111" : { 74 | "connection_type" : "Cable/DSL" 75 | } 76 | }, 77 | { 78 | "::96.69.0.0/112" : { 79 | "connection_type" : "Cable/DSL" 80 | } 81 | }, 82 | { 83 | "::96.94.0.0/111" : { 84 | "connection_type" : "Cable/DSL" 85 | } 86 | }, 87 | { 88 | "::108.96.0.0/107" : { 89 | "connection_type" : "Cellular" 90 | } 91 | }, 92 | { 93 | "::175.16.199.0/120" : { 94 | "connection_type" : "Cable/DSL" 95 | } 96 | }, 97 | { 98 | "::187.156.138.0/120" : { 99 | "connection_type" : "Cable/DSL" 100 | } 101 | }, 102 | { 103 | "::201.243.200.0/120" : { 104 | "connection_type" : "Corporate" 105 | } 106 | }, 107 | { 108 | "::207.179.48.0/116" : { 109 | "connection_type" : "Cellular" 110 | } 111 | }, 112 | { 113 | "::214.78.120.0/118" : { 114 | "connection_type" : "Satellite" 115 | } 116 | }, 117 | { 118 | "::216.160.83.56/125" : { 119 | "connection_type" : "Corporate" 120 | } 121 | }, 122 | { 123 | "2003::/24" : { 124 | "connection_type" : "Cable/DSL" 125 | } 126 | } 127 | ] 128 | -------------------------------------------------------------------------------- /source-data/GeoIP2-DensityIncome-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "::5.83.124.0/118" : { 4 | "average_income" : 32323, 5 | "population_density" : 1232 6 | } 7 | }, 8 | { 9 | "::216.160.83.0/120" : { 10 | "average_income" : 24626, 11 | "population_density" : 1341 12 | } 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /source-data/GeoIP2-Domain-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "::1.2.0.0/112" : { 4 | "domain" : "maxmind.com" 5 | } 6 | }, 7 | { 8 | "::71.160.223.0/120" : { 9 | "domain" : "verizon.net" 10 | } 11 | }, 12 | { 13 | "::186.30.236.0/120" : { 14 | "domain" : "replaced.com" 15 | } 16 | }, 17 | { 18 | "2a02:2770:3::/64" : { 19 | "domain" : "sgotti.org" 20 | } 21 | }, 22 | { 23 | "2a02:8420:48f4:b000::/64" : { 24 | "domain" : "sfr.net" 25 | } 26 | }, 27 | { 28 | "::142.217.194.0/119" : { 29 | "domain" : "telebecinternet.net" 30 | } 31 | }, 32 | { 33 | "::142.217.196.0/118" : { 34 | "domain" : "telebecinternet.net" 35 | } 36 | }, 37 | { 38 | "::142.217.200.0/117" : { 39 | "domain" : "telebecinternet.net" 40 | } 41 | }, 42 | { 43 | "::142.217.208.0/118" : { 44 | "domain" : "telebecinternet.net" 45 | } 46 | }, 47 | { 48 | "::142.217.212.0/119" : { 49 | "domain" : "telebecinternet.net" 50 | } 51 | }, 52 | { 53 | "::142.217.214.0/120" : { 54 | "domain" : "telebecinternet.net" 55 | } 56 | }, 57 | { 58 | "::208.110.201.0/120" : { 59 | "domain" : "comcastbusiness.net" 60 | } 61 | }, 62 | { 63 | "::208.110.202.0/119" : { 64 | "domain" : "comcastbusiness.net" 65 | } 66 | }, 67 | { 68 | "::208.110.204.0/118" : { 69 | "domain" : "comcastbusiness.net" 70 | } 71 | }, 72 | { 73 | "::208.110.208.0/116" : { 74 | "domain" : "comcastbusiness.net" 75 | } 76 | }, 77 | { 78 | "::214.78.120.0/118" : { 79 | "domain" : "starlinkisp.net" 80 | } 81 | }, 82 | { 83 | "::222.230.136.0/118" : { 84 | "domain" : "gol.ne.jp" 85 | } 86 | }, 87 | { 88 | "::65.115.240.0/116" : { 89 | "domain" : "qwest.net" 90 | } 91 | }, 92 | { 93 | "::65.116.0.0/118" : { 94 | "domain" : "qwest.net" 95 | } 96 | }, 97 | { 98 | "::65.116.4.0/120" : { 99 | "domain" : "qwest.net" 100 | } 101 | }, 102 | { 103 | "::65.23.96.0/115" : { 104 | "domain" : "nuvox.net" 105 | } 106 | }, 107 | { 108 | "::66.92.78.0/119" : { 109 | "domain" : "speakeasy.net" 110 | } 111 | }, 112 | { 113 | "::66.92.80.0/116" : { 114 | "domain" : "speakeasy.net" 115 | } 116 | }, 117 | { 118 | "::66.92.96.0/115" : { 119 | "domain" : "speakeasy.net" 120 | } 121 | }, 122 | { 123 | "::66.92.128.0/113" : { 124 | "domain" : "speakeasy.net" 125 | } 126 | }, 127 | { 128 | "::66.93.0.0/113" : { 129 | "domain" : "speakeasy.net" 130 | } 131 | }, 132 | { 133 | "::66.93.128.0/114" : { 134 | "domain" : "speakeasy.net" 135 | } 136 | }, 137 | { 138 | "::66.93.192.0/115" : { 139 | "domain" : "speakeasy.net" 140 | } 141 | }, 142 | { 143 | "::66.93.224.0/116" : { 144 | "domain" : "speakeasy.net" 145 | } 146 | }, 147 | { 148 | "::66.93.240.0/117" : { 149 | "domain" : "speakeasy.net" 150 | } 151 | }, 152 | { 153 | "::66.93.248.0/119" : { 154 | "domain" : "speakeasy.net" 155 | } 156 | }, 157 | { 158 | "::67.43.156.0/120" : { 159 | "domain" : "shoesfin.NET" 160 | } 161 | }, 162 | { 163 | "::69.218.198.0/119" : { 164 | "domain" : "ameritech.net" 165 | } 166 | }, 167 | { 168 | "::69.218.200.0/117" : { 169 | "domain" : "ameritech.net" 170 | } 171 | }, 172 | { 173 | "::69.218.208.0/116" : { 174 | "domain" : "ameritech.net" 175 | } 176 | }, 177 | { 178 | "::69.218.224.0/115" : { 179 | "domain" : "ameritech.net" 180 | } 181 | }, 182 | { 183 | "::69.219.0.0/114" : { 184 | "domain" : "ameritech.net" 185 | } 186 | }, 187 | { 188 | "::69.219.64.0/116" : { 189 | "domain" : "ameritech.net" 190 | } 191 | }, 192 | { 193 | "::69.219.80.0/118" : { 194 | "domain" : "ameritech.net" 195 | } 196 | }, 197 | { 198 | "::69.219.84.0/120" : { 199 | "domain" : "ameritech.net" 200 | } 201 | }, 202 | { 203 | "::70.46.0.0/113" : { 204 | "domain" : "nuvox.net" 205 | } 206 | }, 207 | { 208 | "::70.46.128.0/118" : { 209 | "domain" : "nuvox.net" 210 | } 211 | }, 212 | { 213 | "::70.46.132.0/120" : { 214 | "domain" : "nuvox.net" 215 | } 216 | }, 217 | { 218 | "::71.136.2.0/119" : { 219 | "domain" : "pacbell.net" 220 | } 221 | }, 222 | { 223 | "::71.136.4.0/118" : { 224 | "domain" : "pacbell.net" 225 | } 226 | }, 227 | { 228 | "::71.136.8.0/117" : { 229 | "domain" : "pacbell.net" 230 | } 231 | }, 232 | { 233 | "::71.136.16.0/116" : { 234 | "domain" : "pacbell.net" 235 | } 236 | }, 237 | { 238 | "::71.136.32.0/115" : { 239 | "domain" : "pacbell.net" 240 | } 241 | }, 242 | { 243 | "::71.136.64.0/114" : { 244 | "domain" : "pacbell.net" 245 | } 246 | }, 247 | { 248 | "::71.136.128.0/113" : { 249 | "domain" : "pacbell.net" 250 | } 251 | }, 252 | { 253 | "::71.137.0.0/112" : { 254 | "domain" : "pacbell.net" 255 | } 256 | }, 257 | { 258 | "::71.138.0.0/113" : { 259 | "domain" : "pacbell.net" 260 | } 261 | }, 262 | { 263 | "::71.138.128.0/114" : { 264 | "domain" : "pacbell.net" 265 | } 266 | }, 267 | { 268 | "::71.138.192.0/116" : { 269 | "domain" : "pacbell.net" 270 | } 271 | }, 272 | { 273 | "::71.138.208.0/119" : { 274 | "domain" : "pacbell.net" 275 | } 276 | }, 277 | { 278 | "::74.0.88.0/117" : { 279 | "domain" : "covad.net" 280 | } 281 | }, 282 | { 283 | "::74.0.96.0/115" : { 284 | "domain" : "covad.net" 285 | } 286 | }, 287 | { 288 | "::74.0.128.0/113" : { 289 | "domain" : "covad.net" 290 | } 291 | }, 292 | { 293 | "::74.1.0.0/112" : { 294 | "domain" : "covad.net" 295 | } 296 | }, 297 | { 298 | "::74.2.0.0/113" : { 299 | "domain" : "covad.net" 300 | } 301 | }, 302 | { 303 | "::74.2.128.0/115" : { 304 | "domain" : "covad.net" 305 | } 306 | }, 307 | { 308 | "::74.2.160.0/117" : { 309 | "domain" : "covad.net" 310 | } 311 | }, 312 | { 313 | "::74.2.168.0/118" : { 314 | "domain" : "covad.net" 315 | } 316 | }, 317 | { 318 | "::74.2.172.0/119" : { 319 | "domain" : "covad.net" 320 | } 321 | }, 322 | { 323 | "::74.2.174.0/120" : { 324 | "domain" : "covad.net" 325 | } 326 | }, 327 | { 328 | "::75.77.84.0/118" : { 329 | "domain" : "nuvox.net" 330 | } 331 | }, 332 | { 333 | "::75.77.88.0/117" : { 334 | "domain" : "nuvox.net" 335 | } 336 | }, 337 | { 338 | "::75.77.96.0/115" : { 339 | "domain" : "nuvox.net" 340 | } 341 | }, 342 | { 343 | "::75.77.128.0/113" : { 344 | "domain" : "nuvox.net" 345 | } 346 | }, 347 | { 348 | "::78.26.67.0/120" : { 349 | "domain" : "popwifi.it" 350 | } 351 | }, 352 | { 353 | "::78.26.68.0/118" : { 354 | "domain" : "popwifi.it" 355 | } 356 | }, 357 | { 358 | "::78.26.72.0/117" : { 359 | "domain" : "popwifi.it" 360 | } 361 | }, 362 | { 363 | "::78.26.80.0/116" : { 364 | "domain" : "popwifi.it" 365 | } 366 | }, 367 | { 368 | "::78.26.96.0/117" : { 369 | "domain" : "popwifi.it" 370 | } 371 | }, 372 | { 373 | "::78.26.104.0/118" : { 374 | "domain" : "popwifi.it" 375 | } 376 | }, 377 | { 378 | "::78.26.108.0/119" : { 379 | "domain" : "popwifi.it" 380 | } 381 | }, 382 | { 383 | "::78.26.110.0/120" : { 384 | "domain" : "popwifi.it" 385 | } 386 | }, 387 | { 388 | "::81.2.64.0/115" : { 389 | "domain" : "in-addr.arpa" 390 | } 391 | }, 392 | { 393 | "::83.206.0.0/112" : { 394 | "domain" : "oleane.fr" 395 | } 396 | }, 397 | { 398 | "::89.160.0.0/116" : { 399 | "domain" : "bredband2.com" 400 | } 401 | }, 402 | { 403 | "::89.160.16.0/117" : { 404 | "domain" : "bredband2.com" 405 | } 406 | }, 407 | { 408 | "::89.160.24.0/118" : { 409 | "domain" : "bredband2.com" 410 | } 411 | }, 412 | { 413 | "::89.160.28.0/119" : { 414 | "domain" : "bredband2.com" 415 | } 416 | }, 417 | { 418 | "::89.160.30.0/120" : { 419 | "domain" : "bredband2.com" 420 | } 421 | }, 422 | { 423 | "::89.92.0.0/111" : { 424 | "domain" : "bbox.fr" 425 | } 426 | }, 427 | { 428 | "::89.94.0.0/112" : { 429 | "domain" : "bbox.fr" 430 | } 431 | }, 432 | { 433 | "::89.95.0.0/113" : { 434 | "domain" : "bbox.fr" 435 | } 436 | }, 437 | { 438 | "::89.95.128.0/114" : { 439 | "domain" : "bbox.fr" 440 | } 441 | }, 442 | { 443 | "::89.95.192.0/115" : { 444 | "domain" : "bbox.fr" 445 | } 446 | }, 447 | { 448 | "::89.95.224.0/116" : { 449 | "domain" : "bbox.fr" 450 | } 451 | }, 452 | { 453 | "::89.95.240.0/119" : { 454 | "domain" : "bbox.fr" 455 | } 456 | } 457 | ] 458 | -------------------------------------------------------------------------------- /source-data/GeoIP2-Enterprise-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "::2.125.160.216/125" : { 4 | "city" : { 5 | "confidence" : 50, 6 | "geoname_id" : 2655045, 7 | "names" : { 8 | "en" : "Boxford" 9 | } 10 | }, 11 | "continent" : { 12 | "code" : "EU", 13 | "geoname_id" : 6255148, 14 | "names" : { 15 | "de" : "Europa", 16 | "en" : "Europe", 17 | "es" : "Europa", 18 | "fr" : "Europe", 19 | "ja" : "ヨーロッパ", 20 | "pt-BR" : "Europa", 21 | "ru" : "Европа", 22 | "zh-CN" : "欧洲" 23 | } 24 | }, 25 | "country" : { 26 | "confidence" : 95, 27 | "geoname_id" : 2635167, 28 | "iso_code" : "GB", 29 | "names" : { 30 | "de" : "Vereinigtes Königreich", 31 | "en" : "United Kingdom", 32 | "es" : "Reino Unido", 33 | "fr" : "Royaume-Uni", 34 | "ja" : "イギリス", 35 | "pt-BR" : "Reino Unido", 36 | "ru" : "Великобритания", 37 | "zh-CN" : "英国" 38 | } 39 | }, 40 | "location" : { 41 | "accuracy_radius" : 100, 42 | "latitude" : 51.75, 43 | "longitude" : -1.25, 44 | "time_zone" : "Europe/London" 45 | }, 46 | "postal" : { 47 | "code" : "OX1", 48 | "confidence" : 20 49 | }, 50 | "registered_country" : { 51 | "geoname_id" : 3017382, 52 | "is_in_european_union" : true, 53 | "iso_code" : "FR", 54 | "names" : { 55 | "de" : "Frankreich", 56 | "en" : "France", 57 | "es" : "Francia", 58 | "fr" : "France", 59 | "ja" : "フランス共和国", 60 | "pt-BR" : "França", 61 | "ru" : "Франция", 62 | "zh-CN" : "法国" 63 | } 64 | }, 65 | "subdivisions" : [ 66 | { 67 | "confidence" : 70, 68 | "geoname_id" : 6269131, 69 | "iso_code" : "ENG", 70 | "names" : { 71 | "en" : "England", 72 | "es" : "Inglaterra", 73 | "fr" : "Angleterre", 74 | "pt-BR" : "Inglaterra" 75 | } 76 | }, 77 | { 78 | "confidence" : 60, 79 | "geoname_id" : 3333217, 80 | "iso_code" : "WBK", 81 | "names" : { 82 | "en" : "West Berkshire", 83 | "ru" : "Западный Беркшир", 84 | "zh-CN" : "西伯克郡" 85 | } 86 | } 87 | ], 88 | "traits" : { 89 | "static_ip_score" : 0.27 90 | } 91 | } 92 | }, 93 | { 94 | "::67.43.156.0/120" : { 95 | "continent" : { 96 | "code" : "AS", 97 | "geoname_id" : 6255147, 98 | "names" : { 99 | "de" : "Asien", 100 | "en" : "Asia", 101 | "es" : "Asia", 102 | "fr" : "Asie", 103 | "ja" : "アジア", 104 | "pt-BR" : "Ásia", 105 | "ru" : "Азия", 106 | "zh-CN" : "亚洲" 107 | } 108 | }, 109 | "country" : { 110 | "confidence" : 99, 111 | "geoname_id" : 1252634, 112 | "iso_code" : "BT", 113 | "names" : { 114 | "de" : "Bhutan", 115 | "en" : "Bhutan", 116 | "es" : "Bután", 117 | "fr" : "Bhutan", 118 | "ja" : "ブータン王国", 119 | "pt-BR" : "Butão", 120 | "ru" : "Бутан", 121 | "zh-CN" : "不丹" 122 | } 123 | }, 124 | "location" : { 125 | "accuracy_radius" : 534, 126 | "latitude" : 27.5, 127 | "longitude" : 90.5, 128 | "time_zone" : "Asia/Thimphu" 129 | }, 130 | "registered_country" : { 131 | "geoname_id" : 798549, 132 | "is_in_european_union" : true, 133 | "iso_code" : "RO", 134 | "names" : { 135 | "de" : "Rumänien", 136 | "en" : "Romania", 137 | "es" : "Rumanía", 138 | "fr" : "Roumanie", 139 | "ja" : "ルーマニア", 140 | "pt-BR" : "Romênia", 141 | "ru" : "Румыния", 142 | "zh-CN" : "罗马尼亚" 143 | } 144 | }, 145 | "traits" : { 146 | "autonomous_system_number" : 35908, 147 | "domain" : "shoesfin.NET", 148 | "isp" : "Loud Packet", 149 | "organization" : "zudoarichikito_", 150 | "static_ip_score" : 0.34, 151 | "user_type" : "search_engine_spider" 152 | } 153 | } 154 | }, 155 | { 156 | "::74.209.24.0/116" : { 157 | "city" : { 158 | "confidence" : 11, 159 | "geoname_id" : 5112335, 160 | "names" : { 161 | "en" : "Chatham" 162 | } 163 | }, 164 | "continent" : { 165 | "code" : "NA", 166 | "geoname_id" : 6255149, 167 | "names" : { 168 | "de" : "Nordamerika", 169 | "en" : "North America", 170 | "es" : "Norteamérica", 171 | "fr" : "Amérique du Nord", 172 | "ja" : "北アメリカ", 173 | "pt-BR" : "América do Norte", 174 | "ru" : "Северная Америка", 175 | "zh-CN" : "北美洲" 176 | } 177 | }, 178 | "country" : { 179 | "confidence" : 99, 180 | "geoname_id" : 6252001, 181 | "iso_code" : "US", 182 | "names" : { 183 | "de" : "USA", 184 | "en" : "United States", 185 | "es" : "Estados Unidos", 186 | "fr" : "États-Unis", 187 | "ja" : "アメリカ合衆国", 188 | "pt-BR" : "Estados Unidos", 189 | "ru" : "США", 190 | "zh-CN" : "美国" 191 | } 192 | }, 193 | "location" : { 194 | "accuracy_radius" : 27, 195 | "latitude" : 42.3478, 196 | "longitude" : -73.5549, 197 | "metro_code" : 532, 198 | "time_zone" : "America/New_York" 199 | }, 200 | "postal" : { 201 | "code" : "12037", 202 | "confidence" : 11 203 | }, 204 | "registered_country" : { 205 | "geoname_id" : 6252001, 206 | "iso_code" : "US", 207 | "names" : { 208 | "de" : "USA", 209 | "en" : "United States", 210 | "es" : "Estados Unidos", 211 | "fr" : "États-Unis", 212 | "ja" : "アメリカ合衆国", 213 | "pt-BR" : "Estados Unidos", 214 | "ru" : "США", 215 | "zh-CN" : "美国" 216 | } 217 | }, 218 | "subdivisions" : [ 219 | { 220 | "confidence" : 93, 221 | "geoname_id" : 5128638, 222 | "iso_code" : "NY", 223 | "names" : { 224 | "de" : "New York", 225 | "en" : "New York", 226 | "es" : "Nueva York", 227 | "fr" : "New York", 228 | "ja" : "ニューヨーク州", 229 | "pt-BR" : "Nova Iorque", 230 | "ru" : "Нью-Йорк", 231 | "zh-CN" : "纽约州" 232 | } 233 | } 234 | ], 235 | "traits" : { 236 | "autonomous_system_number" : 14671, 237 | "autonomous_system_organization" : "FairPoint Communications", 238 | "connection_type" : "Cable/DSL", 239 | "domain" : "frpt.net", 240 | "is_legitimate_proxy" : true, 241 | "isp" : "Fairpoint Communications", 242 | "organization" : "Fairpoint Communications", 243 | "static_ip_score" : 0.34, 244 | "user_type" : "residential" 245 | } 246 | } 247 | }, 248 | { 249 | "::81.2.69.160/123" : { 250 | "city" : { 251 | "confidence" : 42, 252 | "geoname_id" : 2643743, 253 | "names" : { 254 | "de" : "London", 255 | "en" : "London", 256 | "es" : "Londres", 257 | "fr" : "Londres", 258 | "ja" : "ロンドン", 259 | "pt-BR" : "Londres", 260 | "ru" : "Лондон" 261 | } 262 | }, 263 | "continent" : { 264 | "code" : "EU", 265 | "geoname_id" : 6255148, 266 | "names" : { 267 | "de" : "Europa", 268 | "en" : "Europe", 269 | "es" : "Europa", 270 | "fr" : "Europe", 271 | "ja" : "ヨーロッパ", 272 | "pt-BR" : "Europa", 273 | "ru" : "Европа", 274 | "zh-CN" : "欧洲" 275 | } 276 | }, 277 | "country" : { 278 | "confidence" : 99, 279 | "geoname_id" : 2635167, 280 | "iso_code" : "GB", 281 | "names" : { 282 | "de" : "Vereinigtes Königreich", 283 | "en" : "United Kingdom", 284 | "es" : "Reino Unido", 285 | "fr" : "Royaume-Uni", 286 | "ja" : "イギリス", 287 | "pt-BR" : "Reino Unido", 288 | "ru" : "Великобритания", 289 | "zh-CN" : "英国" 290 | } 291 | }, 292 | "location" : { 293 | "accuracy_radius" : 100, 294 | "latitude" : 51.5142, 295 | "longitude" : -0.0931, 296 | "time_zone" : "Europe/London" 297 | }, 298 | "registered_country" : { 299 | "geoname_id" : 6252001, 300 | "iso_code" : "US", 301 | "names" : { 302 | "de" : "USA", 303 | "en" : "United States", 304 | "es" : "Estados Unidos", 305 | "fr" : "États-Unis", 306 | "ja" : "アメリカ合衆国", 307 | "pt-BR" : "Estados Unidos", 308 | "ru" : "США", 309 | "zh-CN" : "美国" 310 | } 311 | }, 312 | "subdivisions" : [ 313 | { 314 | "confidence" : 42, 315 | "geoname_id" : 6269131, 316 | "iso_code" : "ENG", 317 | "names" : { 318 | "en" : "England", 319 | "es" : "Inglaterra", 320 | "fr" : "Angleterre", 321 | "pt-BR" : "Inglaterra" 322 | } 323 | } 324 | ], 325 | "traits" : { 326 | "connection_type" : "Corporate", 327 | "domain" : "in-addr.arpa", 328 | "isp" : "Andrews & Arnold Ltd", 329 | "organization" : "STONEHOUSE office network", 330 | "static_ip_score" : 0.34, 331 | "user_type" : "government" 332 | } 333 | } 334 | }, 335 | { 336 | "::89.160.20.112/124" : { 337 | "city" : { 338 | "confidence" : 51, 339 | "geoname_id" : 2694762, 340 | "names" : { 341 | "de" : "Linköping", 342 | "en" : "Linköping", 343 | "fr" : "Linköping", 344 | "ja" : "リンシェーピング", 345 | "zh-CN" : "林雪平" 346 | } 347 | }, 348 | "continent" : { 349 | "code" : "EU", 350 | "geoname_id" : 6255148, 351 | "names" : { 352 | "de" : "Europa", 353 | "en" : "Europe", 354 | "es" : "Europa", 355 | "fr" : "Europe", 356 | "ja" : "ヨーロッパ", 357 | "pt-BR" : "Europa", 358 | "ru" : "Европа", 359 | "zh-CN" : "欧洲" 360 | } 361 | }, 362 | "country" : { 363 | "confidence" : 99, 364 | "geoname_id" : 2661886, 365 | "is_in_european_union" : true, 366 | "iso_code" : "SE", 367 | "names" : { 368 | "de" : "Schweden", 369 | "en" : "Sweden", 370 | "es" : "Suecia", 371 | "fr" : "Suède", 372 | "ja" : "スウェーデン王国", 373 | "pt-BR" : "Suécia", 374 | "ru" : "Швеция", 375 | "zh-CN" : "瑞典" 376 | } 377 | }, 378 | "location" : { 379 | "accuracy_radius" : 76, 380 | "latitude" : 58.4167, 381 | "longitude" : 15.6167, 382 | "time_zone" : "Europe/Stockholm" 383 | }, 384 | "registered_country" : { 385 | "geoname_id" : 2921044, 386 | "is_in_european_union" : true, 387 | "iso_code" : "DE", 388 | "names" : { 389 | "de" : "Deutschland", 390 | "en" : "Germany", 391 | "es" : "Alemania", 392 | "fr" : "Allemagne", 393 | "ja" : "ドイツ連邦共和国", 394 | "pt-BR" : "Alemanha", 395 | "ru" : "Германия", 396 | "zh-CN" : "德国" 397 | } 398 | }, 399 | "subdivisions" : [ 400 | { 401 | "confidence" : 51, 402 | "geoname_id" : 2685867, 403 | "iso_code" : "E", 404 | "names" : { 405 | "en" : "Östergötland County", 406 | "fr" : "Comté d'Östergötland" 407 | } 408 | } 409 | ], 410 | "traits" : { 411 | "autonomous_system_number" : 29518, 412 | "autonomous_system_organization" : "Bredband2 AB", 413 | "connection_type" : "Corporate", 414 | "domain" : "bredband2.com", 415 | "isp" : "Bredband2 AB", 416 | "organization" : "Bevtec", 417 | "static_ip_score" : 0.34, 418 | "user_type" : "government" 419 | } 420 | } 421 | }, 422 | { 423 | "::149.101.100.0/124" : { 424 | "continent" : { 425 | "code" : "NA", 426 | "geoname_id" : 6255149, 427 | "names" : { 428 | "de" : "Nordamerika", 429 | "en" : "North America", 430 | "es" : "América del Norte", 431 | "fr" : "Amérique du Nord", 432 | "ja" : "北アメリカ", 433 | "pt-BR" : "América do Norte", 434 | "ru" : "Северная Америка", 435 | "zh-CN" : "北美洲" 436 | } 437 | }, 438 | "country" : { 439 | "confidence" : 99, 440 | "geoname_id" : 6252001, 441 | "iso_code" : "US", 442 | "names" : { 443 | "de" : "USA", 444 | "en" : "United States", 445 | "es" : "Estados Unidos", 446 | "fr" : "États-Unis", 447 | "ja" : "アメリカ合衆国", 448 | "pt-BR" : "Estados Unidos", 449 | "ru" : "США", 450 | "zh-CN" : "美国" 451 | } 452 | }, 453 | "location" : { 454 | "accuracy_radius" : 1000, 455 | "latitude" : 37.751, 456 | "longitude" : -97.822, 457 | "time_zone" : "America/Chicago" 458 | }, 459 | "registered_country" : { 460 | "geoname_id" : 2635167, 461 | "iso_code" : "GB", 462 | "names" : { 463 | "de" : "Vereinigtes Königreich", 464 | "en" : "United Kingdom", 465 | "es" : "Reino Unido", 466 | "fr" : "Royaume-Uni", 467 | "ja" : "イギリス", 468 | "pt-BR" : "Reino Unido", 469 | "ru" : "Великобритания", 470 | "zh-CN" : "英国" 471 | } 472 | }, 473 | "traits" : { 474 | "autonomous_system_number" : 6167, 475 | "autonomous_system_organization" : "CELLCO-PART", 476 | "isp" : "Verizon Wireless", 477 | "mobile_country_code" : "310", 478 | "mobile_network_code" : "004", 479 | "organization" : "Verizon Wireless" 480 | } 481 | } 482 | }, 483 | { 484 | "::175.16.199.0/120" : { 485 | "city" : { 486 | "confidence" : 50, 487 | "geoname_id" : 2038180, 488 | "names" : { 489 | "de" : "Chángchūn", 490 | "en" : "Changchun", 491 | "fr" : "Changchun", 492 | "ja" : "長春市", 493 | "ru" : "Чанчунь", 494 | "zh-CN" : "长春" 495 | } 496 | }, 497 | "continent" : { 498 | "code" : "AS", 499 | "geoname_id" : 6255147, 500 | "names" : { 501 | "de" : "Asien", 502 | "en" : "Asia", 503 | "es" : "Asia", 504 | "fr" : "Asie", 505 | "ja" : "アジア", 506 | "pt-BR" : "Ásia", 507 | "ru" : "Азия", 508 | "zh-CN" : "亚洲" 509 | } 510 | }, 511 | "country" : { 512 | "confidence" : 95, 513 | "geoname_id" : 1814991, 514 | "iso_code" : "CN", 515 | "names" : { 516 | "de" : "China", 517 | "en" : "China", 518 | "es" : "China", 519 | "fr" : "Chine", 520 | "ja" : "中国", 521 | "pt-BR" : "China", 522 | "ru" : "Китай", 523 | "zh-CN" : "中国" 524 | } 525 | }, 526 | "location" : { 527 | "accuracy_radius" : 100, 528 | "latitude" : 43.88, 529 | "longitude" : 125.3228, 530 | "time_zone" : "Asia/Harbin" 531 | }, 532 | "registered_country" : { 533 | "geoname_id" : 1814991, 534 | "iso_code" : "CN", 535 | "names" : { 536 | "de" : "China", 537 | "en" : "China", 538 | "es" : "China", 539 | "fr" : "Chine", 540 | "ja" : "中国", 541 | "pt-BR" : "China", 542 | "ru" : "Китай", 543 | "zh-CN" : "中国" 544 | } 545 | }, 546 | "subdivisions" : [ 547 | { 548 | "confidence" : 70, 549 | "geoname_id" : 2036500, 550 | "iso_code" : "22", 551 | "names" : { 552 | "en" : "Jilin Sheng", 553 | "zh-CN" : "吉林" 554 | } 555 | } 556 | ], 557 | "traits" : { 558 | "static_ip_score" : 0.63, 559 | "user_type" : "residential" 560 | } 561 | } 562 | }, 563 | { 564 | "::202.196.224.0/116" : { 565 | "continent" : { 566 | "code" : "AS", 567 | "geoname_id" : 6255147, 568 | "names" : { 569 | "de" : "Asien", 570 | "en" : "Asia", 571 | "es" : "Asia", 572 | "fr" : "Asie", 573 | "ja" : "アジア", 574 | "pt-BR" : "Ásia", 575 | "ru" : "Азия", 576 | "zh-CN" : "亚洲" 577 | } 578 | }, 579 | "country" : { 580 | "confidence" : 95, 581 | "geoname_id" : 1694008, 582 | "iso_code" : "PH", 583 | "names" : { 584 | "de" : "Philippinen", 585 | "en" : "Philippines", 586 | "es" : "Filipinas", 587 | "fr" : "Philippines", 588 | "ja" : "フィリピン共和国", 589 | "pt-BR" : "Filipinas", 590 | "ru" : "Филиппины", 591 | "zh-CN" : "菲律宾" 592 | } 593 | }, 594 | "location" : { 595 | "accuracy_radius" : 121, 596 | "latitude" : 13, 597 | "longitude" : 122, 598 | "time_zone" : "Asia/Manila" 599 | }, 600 | "postal" : { 601 | "code" : "34021", 602 | "confidence" : 20 603 | }, 604 | "registered_country" : { 605 | "geoname_id" : 1694008, 606 | "iso_code" : "PH", 607 | "names" : { 608 | "de" : "Philippinen", 609 | "en" : "Philippines", 610 | "es" : "Filipinas", 611 | "fr" : "Philippines", 612 | "ja" : "フィリピン共和国", 613 | "pt-BR" : "Filipinas", 614 | "ru" : "Филиппины", 615 | "zh-CN" : "菲律宾" 616 | } 617 | }, 618 | "represented_country" : { 619 | "geoname_id" : 6252001, 620 | "iso_code" : "US", 621 | "names" : { 622 | "de" : "USA", 623 | "en" : "United States", 624 | "es" : "Estados Unidos", 625 | "fr" : "États-Unis", 626 | "ja" : "アメリカ合衆国", 627 | "pt-BR" : "Estados Unidos", 628 | "ru" : "США", 629 | "zh-CN" : "美国" 630 | }, 631 | "type" : "military" 632 | }, 633 | "traits" : { 634 | "static_ip_score" : 1.29 635 | } 636 | } 637 | }, 638 | { 639 | "::216.160.83.56/125" : { 640 | "city" : { 641 | "confidence" : 40, 642 | "geoname_id" : 5803556, 643 | "names" : { 644 | "en" : "Milton", 645 | "ru" : "Мильтон" 646 | } 647 | }, 648 | "continent" : { 649 | "code" : "NA", 650 | "geoname_id" : 6255149, 651 | "names" : { 652 | "de" : "Nordamerika", 653 | "en" : "North America", 654 | "es" : "América del Norte", 655 | "fr" : "Amérique du Nord", 656 | "ja" : "北アメリカ", 657 | "pt-BR" : "América do Norte", 658 | "ru" : "Северная Америка", 659 | "zh-CN" : "北美洲" 660 | } 661 | }, 662 | "country" : { 663 | "confidence" : 99, 664 | "geoname_id" : 6252001, 665 | "iso_code" : "US", 666 | "names" : { 667 | "de" : "USA", 668 | "en" : "United States", 669 | "es" : "Estados Unidos", 670 | "fr" : "États-Unis", 671 | "ja" : "アメリカ合衆国", 672 | "pt-BR" : "Estados Unidos", 673 | "ru" : "США", 674 | "zh-CN" : "美国" 675 | } 676 | }, 677 | "location" : { 678 | "accuracy_radius" : 22, 679 | "latitude" : 47.2513, 680 | "longitude" : -122.3149, 681 | "metro_code" : 819, 682 | "time_zone" : "America/Los_Angeles" 683 | }, 684 | "postal" : { 685 | "code" : "98354", 686 | "confidence" : 40 687 | }, 688 | "registered_country" : { 689 | "geoname_id" : 2635167, 690 | "iso_code" : "GB", 691 | "names" : { 692 | "de" : "Vereinigtes Königreich", 693 | "en" : "United Kingdom", 694 | "es" : "Reino Unido", 695 | "fr" : "Royaume-Uni", 696 | "ja" : "イギリス", 697 | "pt-BR" : "Reino Unido", 698 | "ru" : "Великобритания", 699 | "zh-CN" : "英国" 700 | } 701 | }, 702 | "subdivisions" : [ 703 | { 704 | "confidence" : 99, 705 | "geoname_id" : 5815135, 706 | "iso_code" : "WA", 707 | "names" : { 708 | "en" : "Washington", 709 | "es" : "Washington", 710 | "fr" : "État de Washington", 711 | "ja" : "ワシントン州", 712 | "ru" : "Вашингтон", 713 | "zh-CN" : "华盛顿州" 714 | } 715 | } 716 | ], 717 | "traits" : { 718 | "autonomous_system_number" : 209, 719 | "connection_type" : "Cable/DSL", 720 | "isp" : "Century Link", 721 | "organization" : "Lariat Software", 722 | "static_ip_score" : 1.47, 723 | "user_type" : "government" 724 | } 725 | } 726 | }, 727 | { 728 | "2001:480::/64" : { 729 | "continent" : { 730 | "code" : "AS", 731 | "geoname_id" : 6255147, 732 | "names" : { 733 | "de" : "Asien", 734 | "en" : "Asia", 735 | "es" : "Asia", 736 | "fr" : "Asie", 737 | "ja" : "アジア", 738 | "pt-BR" : "Ásia", 739 | "ru" : "Азия", 740 | "zh-CN" : "亚洲" 741 | } 742 | }, 743 | "country" : { 744 | "geoname_id" : 1861060, 745 | "iso_code" : "JP", 746 | "names" : { 747 | "de" : "Japan", 748 | "en" : "Japan", 749 | "es" : "Japón", 750 | "fr" : "Japon", 751 | "ja" : "日本", 752 | "pt-BR" : "Japão", 753 | "ru" : "Япония", 754 | "zh-CN" : "日本" 755 | } 756 | }, 757 | "location" : { 758 | "accuracy_radius" : 100, 759 | "latitude" : 35.68536, 760 | "longitude" : 139.75309, 761 | "time_zone" : "Asia/Tokyo" 762 | }, 763 | "registered_country" : { 764 | "geoname_id" : 1861060, 765 | "iso_code" : "JP", 766 | "names" : { 767 | "de" : "Japan", 768 | "en" : "Japan", 769 | "es" : "Japón", 770 | "fr" : "Japon", 771 | "ja" : "日本", 772 | "pt-BR" : "Japão", 773 | "ru" : "Япония", 774 | "zh-CN" : "日本" 775 | } 776 | } 777 | } 778 | }, 779 | { 780 | "214.1.1.0/24" : { 781 | "traits" : { 782 | "is_anycast" : true 783 | } 784 | } 785 | }, 786 | { 787 | "214.78.0.0/23" : { 788 | "city" : { 789 | "confidence" : 99, 790 | "geoname_id" : 5391811, 791 | "names" : { 792 | "de" : "San Diego", 793 | "en" : "San Diego", 794 | "es" : "San Diego", 795 | "fr" : "San Diego", 796 | "ja" : "サンディエゴ", 797 | "pt-BR" : "San Diego", 798 | "ru" : "Сан-Диего" 799 | } 800 | }, 801 | "continent" : { 802 | "code" : "NA", 803 | "geoname_id" : 6255149, 804 | "names" : { 805 | "de" : "Nordamerika", 806 | "en" : "North America", 807 | "es" : "Norteamérica", 808 | "fr" : "Amérique du Nord", 809 | "ja" : "北アメリカ", 810 | "pt-BR" : "América do Norte", 811 | "ru" : "Северная Америка", 812 | "zh-CN" : "北美洲" 813 | } 814 | }, 815 | "country" : { 816 | "confidence" : 99, 817 | "geoname_id" : 6252001, 818 | "iso_code" : "US", 819 | "names" : { 820 | "de" : "Vereinigte Staaten", 821 | "en" : "United States", 822 | "es" : "Estados Unidos", 823 | "fr" : "États Unis", 824 | "ja" : "アメリカ", 825 | "pt-BR" : "EUA", 826 | "ru" : "США", 827 | "zh-CN" : "美国" 828 | } 829 | }, 830 | "location" : { 831 | "accuracy_radius" : 10, 832 | "latitude" : 32.7449, 833 | "longitude" : -117.165, 834 | "metro_code" : 825, 835 | "time_zone" : "America/Los_Angeles" 836 | }, 837 | "postal" : { 838 | "code" : "92103", 839 | "confidence" : 90 840 | }, 841 | "registered_country" : { 842 | "geoname_id" : 6252001, 843 | "iso_code" : "US", 844 | "names" : { 845 | "de" : "Vereinigte Staaten", 846 | "en" : "United States", 847 | "es" : "Estados Unidos", 848 | "fr" : "États Unis", 849 | "ja" : "アメリカ", 850 | "pt-BR" : "EUA", 851 | "ru" : "США", 852 | "zh-CN" : "美国" 853 | } 854 | }, 855 | "subdivisions" : [ 856 | { 857 | "confidence" : 99, 858 | "geoname_id" : 5332921, 859 | "iso_code" : "CA", 860 | "names" : { 861 | "de" : "Kalifornien", 862 | "en" : "California", 863 | "es" : "California", 864 | "fr" : "Californie", 865 | "ja" : "カリフォルニア州", 866 | "pt-BR" : "Califórnia", 867 | "ru" : "Калифорния", 868 | "zh-CN" : "加州" 869 | } 870 | } 871 | ], 872 | "traits" : { 873 | "autonomous_system_number" : 5972, 874 | "autonomous_system_organization" : "DNIC-ASBLK-05800-06055", 875 | "connection_type" : "Cable/DSL", 876 | "domain" : "health.mil", 877 | "isp" : "US Air Force", 878 | "organization" : "US Department of Defense", 879 | "user_type" : "military" 880 | } 881 | } 882 | }, 883 | { 884 | "214.78.120.0/22" : { 885 | "city" : { 886 | "geoname_id" : 5391811, 887 | "names" : { 888 | "de" : "San Diego", 889 | "en" : "San Diego", 890 | "es" : "San Diego", 891 | "fr" : "San Diego", 892 | "ja" : "サンディエゴ", 893 | "pt-BR" : "San Diego", 894 | "ru" : "Сан-Диего" 895 | } 896 | }, 897 | "continent" : { 898 | "code" : "NA", 899 | "geoname_id" : 6255149, 900 | "names" : { 901 | "de" : "Nordamerika", 902 | "en" : "North America", 903 | "es" : "Norteamérica", 904 | "fr" : "Amérique du Nord", 905 | "ja" : "北アメリカ", 906 | "pt-BR" : "América do Norte", 907 | "ru" : "Северная Америка", 908 | "zh-CN" : "北美洲" 909 | } 910 | }, 911 | "country" : { 912 | "geoname_id" : 6252001, 913 | "iso_code" : "US", 914 | "names" : { 915 | "de" : "Vereinigte Staaten", 916 | "en" : "United States", 917 | "es" : "Estados Unidos", 918 | "fr" : "États Unis", 919 | "ja" : "アメリカ", 920 | "pt-BR" : "EUA", 921 | "ru" : "США", 922 | "zh-CN" : "美国" 923 | } 924 | }, 925 | "location" : { 926 | "accuracy_radius" : 100, 927 | "latitude" : 32.7405, 928 | "longitude" : -117.0935, 929 | "metro_code" : 825, 930 | "time_zone" : "America/Los_Angeles" 931 | }, 932 | "postal" : { 933 | "code" : "92105" 934 | }, 935 | "registered_country" : { 936 | "geoname_id" : 6252001, 937 | "iso_code" : "US", 938 | "names" : { 939 | "de" : "Vereinigte Staaten", 940 | "en" : "United States", 941 | "es" : "Estados Unidos", 942 | "fr" : "États Unis", 943 | "ja" : "アメリカ", 944 | "pt-BR" : "EUA", 945 | "ru" : "США", 946 | "zh-CN" : "美国" 947 | } 948 | }, 949 | "subdivisions" : [ 950 | { 951 | "geoname_id" : 5332921, 952 | "iso_code" : "CA", 953 | "names" : { 954 | "de" : "Kalifornien", 955 | "en" : "California", 956 | "es" : "California", 957 | "fr" : "Californie", 958 | "ja" : "カリフォルニア州", 959 | "pt-BR" : "Califórnia", 960 | "ru" : "Калифорния", 961 | "zh-CN" : "加州" 962 | } 963 | } 964 | ], 965 | "traits" : { 966 | "autonomous_system_number" : 14593, 967 | "autonomous_system_organization" : "SPACEX-STARLINK", 968 | "connection_type" : "Satellite", 969 | "domain" : "starlinkisp.net", 970 | "isp" : "Starlink", 971 | "organization" : "Starlink", 972 | "user_type" : "residential" 973 | } 974 | } 975 | }, 976 | { 977 | "2001:480:10::/49" : { 978 | "city" : { 979 | "confidence" : 40, 980 | "geoname_id" : 5391811, 981 | "names" : { 982 | "de" : "San Diego", 983 | "en" : "San Diego", 984 | "es" : "San Diego", 985 | "fr" : "San Diego", 986 | "ja" : "サンディエゴ", 987 | "pt-BR" : "San Diego", 988 | "ru" : "Сан-Диего" 989 | } 990 | }, 991 | "continent" : { 992 | "code" : "NA", 993 | "geoname_id" : 6255149, 994 | "names" : { 995 | "de" : "Nordamerika", 996 | "en" : "North America", 997 | "es" : "Norteamérica", 998 | "fr" : "Amérique du Nord", 999 | "ja" : "北アメリカ", 1000 | "pt-BR" : "América do Norte", 1001 | "ru" : "Северная Америка", 1002 | "zh-CN" : "北美洲" 1003 | } 1004 | }, 1005 | "country" : { 1006 | "confidence" : 99, 1007 | "geoname_id" : 6252001, 1008 | "iso_code" : "US", 1009 | "names" : { 1010 | "de" : "Vereinigte Staaten", 1011 | "en" : "United States", 1012 | "es" : "Estados Unidos", 1013 | "fr" : "États Unis", 1014 | "ja" : "アメリカ", 1015 | "pt-BR" : "EUA", 1016 | "ru" : "США", 1017 | "zh-CN" : "美国" 1018 | } 1019 | }, 1020 | "location" : { 1021 | "accuracy_radius" : 20, 1022 | "latitude" : 32.7203, 1023 | "longitude" : -117.1552, 1024 | "metro_code" : 825, 1025 | "time_zone" : "America/Los_Angeles" 1026 | }, 1027 | "postal" : { 1028 | "code" : "92101", 1029 | "confidence" : 1 1030 | }, 1031 | "registered_country" : { 1032 | "geoname_id" : 6252001, 1033 | "iso_code" : "US", 1034 | "names" : { 1035 | "de" : "Vereinigte Staaten", 1036 | "en" : "United States", 1037 | "es" : "Estados Unidos", 1038 | "fr" : "États Unis", 1039 | "ja" : "アメリカ", 1040 | "pt-BR" : "EUA", 1041 | "ru" : "США", 1042 | "zh-CN" : "美国" 1043 | } 1044 | }, 1045 | "subdivisions" : [ 1046 | { 1047 | "confidence" : 70, 1048 | "geoname_id" : 5332921, 1049 | "iso_code" : "CA", 1050 | "names" : { 1051 | "de" : "Kalifornien", 1052 | "en" : "California", 1053 | "es" : "California", 1054 | "fr" : "Californie", 1055 | "ja" : "カリフォルニア州", 1056 | "pt-BR" : "Califórnia", 1057 | "ru" : "Калифорния", 1058 | "zh-CN" : "加州" 1059 | } 1060 | } 1061 | ], 1062 | "traits" : { 1063 | "autonomous_system_number" : 22, 1064 | "autonomous_system_organization" : "DNIC-AS-00022", 1065 | "connection_type" : "Cable/DSL", 1066 | "domain" : "navy.mil", 1067 | "isp" : "US Department of Defense", 1068 | "organization" : "DNIC", 1069 | "user_type" : "military" 1070 | } 1071 | } 1072 | }, 1073 | { 1074 | "::212.47.235.81/128" : { 1075 | "traits" : { 1076 | "static_ip_score" : 1.47 1077 | } 1078 | } 1079 | }, 1080 | { 1081 | "::212.47.235.82/128" : { 1082 | "traits" : { 1083 | "static_ip_score" : 1.47 1084 | } 1085 | } 1086 | }, 1087 | { 1088 | "214.214.214.216/32" : { 1089 | "traits" : { 1090 | "static_ip_score" : 0.4, 1091 | "user_type" : "traveler" 1092 | } 1093 | } 1094 | }, 1095 | { 1096 | "214.214.214.220/32" : { 1097 | "traits" : { 1098 | "static_ip_score" : 0.4, 1099 | "user_type" : "hosting" 1100 | } 1101 | } 1102 | }, 1103 | { 1104 | "214.214.214.221/32" : { 1105 | "traits" : { 1106 | "user_type" : "content_delivery_network" 1107 | } 1108 | } 1109 | } 1110 | ] 1111 | -------------------------------------------------------------------------------- /source-data/GeoIP2-IP-Risk-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "::214.2.3.0/126" : { 4 | "ip_risk" : 25, 5 | "is_anonymous" : true, 6 | "is_anonymous_vpn" : true 7 | } 8 | }, 9 | { 10 | "::214.2.3.4/128" : { 11 | "ip_risk" : 50, 12 | "is_anonymous" : true, 13 | "is_anonymous_vpn" : true, 14 | "is_tor_exit_node" : true 15 | } 16 | }, 17 | { 18 | "::214.2.3.5/128" : { 19 | "ip_risk" : 90, 20 | "is_anonymous" : true, 21 | "is_anonymous_vpn" : true, 22 | "is_residential_proxy" : true, 23 | "is_tor_exit_node" : true 24 | } 25 | }, 26 | { 27 | "::214.2.3.6/128" : { 28 | "ip_risk" : 85 29 | } 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /source-data/GeoIP2-Precision-Enterprise-Sandbox-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "::23.10.87.103/32" : { 4 | "city" : { 5 | "confidence" : 40, 6 | "geoname_id" : 5368361, 7 | "names" : { 8 | "de" : "Los Angeles", 9 | "en" : "Los Angeles", 10 | "es" : "Los Ángeles", 11 | "fr" : "Los Angeles", 12 | "ja" : "ロサンゼルス", 13 | "pt-BR" : "Los Angeles", 14 | "ru" : "Лос-Анджелес", 15 | "zh-CN" : "洛杉矶" 16 | } 17 | }, 18 | "continent" : { 19 | "code" : "NA", 20 | "geoname_id" : 6255149, 21 | "names" : { 22 | "de" : "Nordamerika", 23 | "en" : "North America", 24 | "es" : "Norteamérica", 25 | "fr" : "Amérique du Nord", 26 | "ja" : "北アメリカ", 27 | "pt-BR" : "América do Norte", 28 | "ru" : "Северная Америка", 29 | "zh-CN" : "北美洲" 30 | } 31 | }, 32 | "country" : { 33 | "confidence" : 99, 34 | "geoname_id" : 6252001, 35 | "iso_code" : "US", 36 | "names" : { 37 | "de" : "USA", 38 | "en" : "United States", 39 | "es" : "Estados Unidos", 40 | "fr" : "États-Unis", 41 | "ja" : "アメリカ合衆国", 42 | "pt-BR" : "Estados Unidos", 43 | "ru" : "США", 44 | "zh-CN" : "美国" 45 | } 46 | }, 47 | "location" : { 48 | "accuracy_radius" : 20, 49 | "latitude" : 34.0544, 50 | "longitude" : -118.244, 51 | "metro_code" : 803, 52 | "time_zone" : "America/Los_Angeles" 53 | }, 54 | "postal" : { 55 | "code" : "90009", 56 | "confidence" : 1 57 | }, 58 | "registered_country" : { 59 | "geoname_id" : 6252001, 60 | "iso_code" : "US", 61 | "names" : { 62 | "de" : "USA", 63 | "en" : "United States", 64 | "es" : "Estados Unidos", 65 | "fr" : "États-Unis", 66 | "ja" : "アメリカ合衆国", 67 | "pt-BR" : "Estados Unidos", 68 | "ru" : "США", 69 | "zh-CN" : "美国" 70 | } 71 | }, 72 | "subdivisions" : [ 73 | { 74 | "confidence" : 90, 75 | "geoname_id" : 5332921, 76 | "iso_code" : "CA", 77 | "names" : { 78 | "de" : "Kalifornien", 79 | "en" : "California", 80 | "es" : "California", 81 | "fr" : "Californie", 82 | "ja" : "カリフォルニア州", 83 | "pt-BR" : "Califórnia", 84 | "ru" : "Калифорния", 85 | "zh-CN" : "加利福尼亚州" 86 | } 87 | } 88 | ], 89 | "traits" : { 90 | "autonomous_system_number" : 33224, 91 | "autonomous_system_organization" : "Towerstream I, Inc.", 92 | "connection_type" : "Corporate", 93 | "isp" : "Towerstream I", 94 | "organization" : "Towerstream I", 95 | "static_ip_score" : 0.34, 96 | "user_type" : "business" 97 | } 98 | } 99 | }, 100 | { 101 | "::152.216.7.110/32" : { 102 | "city" : { 103 | "confidence" : 40, 104 | "geoname_id" : 5368361, 105 | "names" : { 106 | "de" : "Los Angeles", 107 | "en" : "Los Angeles", 108 | "es" : "Los Ángeles", 109 | "fr" : "Los Angeles", 110 | "ja" : "ロサンゼルス", 111 | "pt-BR" : "Los Angeles", 112 | "ru" : "Лос-Анджелес", 113 | "zh-CN" : "洛杉矶" 114 | } 115 | }, 116 | "continent" : { 117 | "code" : "NA", 118 | "geoname_id" : 6255149, 119 | "names" : { 120 | "de" : "Nordamerika", 121 | "en" : "North America", 122 | "es" : "Norteamérica", 123 | "fr" : "Amérique du Nord", 124 | "ja" : "北アメリカ", 125 | "pt-BR" : "América do Norte", 126 | "ru" : "Северная Америка", 127 | "zh-CN" : "北美洲" 128 | } 129 | }, 130 | "country" : { 131 | "confidence" : 99, 132 | "geoname_id" : 6252001, 133 | "iso_code" : "US", 134 | "names" : { 135 | "de" : "USA", 136 | "en" : "United States", 137 | "es" : "Estados Unidos", 138 | "fr" : "États-Unis", 139 | "ja" : "アメリカ合衆国", 140 | "pt-BR" : "Estados Unidos", 141 | "ru" : "США", 142 | "zh-CN" : "美国" 143 | } 144 | }, 145 | "location" : { 146 | "accuracy_radius" : 20, 147 | "latitude" : 34.0544, 148 | "longitude" : -118.244, 149 | "metro_code" : 803, 150 | "time_zone" : "America/Los_Angeles" 151 | }, 152 | "postal" : { 153 | "code" : "90009", 154 | "confidence" : 1 155 | }, 156 | "registered_country" : { 157 | "geoname_id" : 6252001, 158 | "iso_code" : "US", 159 | "names" : { 160 | "de" : "USA", 161 | "en" : "United States", 162 | "es" : "Estados Unidos", 163 | "fr" : "États-Unis", 164 | "ja" : "アメリカ合衆国", 165 | "pt-BR" : "Estados Unidos", 166 | "ru" : "США", 167 | "zh-CN" : "美国" 168 | } 169 | }, 170 | "subdivisions" : [ 171 | { 172 | "confidence" : 90, 173 | "geoname_id" : 5332921, 174 | "iso_code" : "CA", 175 | "names" : { 176 | "de" : "Kalifornien", 177 | "en" : "California", 178 | "es" : "California", 179 | "fr" : "Californie", 180 | "ja" : "カリフォルニア州", 181 | "pt-BR" : "Califórnia", 182 | "ru" : "Калифорния", 183 | "zh-CN" : "加利福尼亚州" 184 | } 185 | } 186 | ], 187 | "traits" : { 188 | "autonomous_system_number" : 33224, 189 | "autonomous_system_organization" : "Towerstream I, Inc.", 190 | "connection_type" : "Corporate", 191 | "isp" : "Towerstream I", 192 | "organization" : "Towerstream I", 193 | "static_ip_score" : 0.34, 194 | "user_type" : "business" 195 | } 196 | } 197 | }, 198 | { 199 | "::146.243.121.22/32" : { 200 | "city" : { 201 | "confidence" : 40, 202 | "geoname_id" : 5368361, 203 | "names" : { 204 | "de" : "Los Angeles", 205 | "en" : "Los Angeles", 206 | "es" : "Los Ángeles", 207 | "fr" : "Los Angeles", 208 | "ja" : "ロサンゼルス", 209 | "pt-BR" : "Los Angeles", 210 | "ru" : "Лос-Анджелес", 211 | "zh-CN" : "洛杉矶" 212 | } 213 | }, 214 | "continent" : { 215 | "code" : "NA", 216 | "geoname_id" : 6255149, 217 | "names" : { 218 | "de" : "Nordamerika", 219 | "en" : "North America", 220 | "es" : "Norteamérica", 221 | "fr" : "Amérique du Nord", 222 | "ja" : "北アメリカ", 223 | "pt-BR" : "América do Norte", 224 | "ru" : "Северная Америка", 225 | "zh-CN" : "北美洲" 226 | } 227 | }, 228 | "country" : { 229 | "confidence" : 99, 230 | "geoname_id" : 6252001, 231 | "iso_code" : "US", 232 | "names" : { 233 | "de" : "USA", 234 | "en" : "United States", 235 | "es" : "Estados Unidos", 236 | "fr" : "États-Unis", 237 | "ja" : "アメリカ合衆国", 238 | "pt-BR" : "Estados Unidos", 239 | "ru" : "США", 240 | "zh-CN" : "美国" 241 | } 242 | }, 243 | "location" : { 244 | "accuracy_radius" : 20, 245 | "latitude" : 34.0544, 246 | "longitude" : -118.244, 247 | "metro_code" : 803, 248 | "time_zone" : "America/Los_Angeles" 249 | }, 250 | "postal" : { 251 | "code" : "90009", 252 | "confidence" : 1 253 | }, 254 | "registered_country" : { 255 | "geoname_id" : 6252001, 256 | "iso_code" : "US", 257 | "names" : { 258 | "de" : "USA", 259 | "en" : "United States", 260 | "es" : "Estados Unidos", 261 | "fr" : "États-Unis", 262 | "ja" : "アメリカ合衆国", 263 | "pt-BR" : "Estados Unidos", 264 | "ru" : "США", 265 | "zh-CN" : "美国" 266 | } 267 | }, 268 | "subdivisions" : [ 269 | { 270 | "confidence" : 90, 271 | "geoname_id" : 5332921, 272 | "iso_code" : "CA", 273 | "names" : { 274 | "de" : "Kalifornien", 275 | "en" : "California", 276 | "es" : "California", 277 | "fr" : "Californie", 278 | "ja" : "カリフォルニア州", 279 | "pt-BR" : "Califórnia", 280 | "ru" : "Калифорния", 281 | "zh-CN" : "加利福尼亚州" 282 | } 283 | } 284 | ], 285 | "traits" : { 286 | "autonomous_system_number" : 33224, 287 | "autonomous_system_organization" : "Towerstream I, Inc.", 288 | "connection_type" : "Corporate", 289 | "isp" : "Towerstream I", 290 | "organization" : "Towerstream I", 291 | "static_ip_score" : 0.34, 292 | "user_type" : "business" 293 | } 294 | } 295 | } 296 | ] 297 | -------------------------------------------------------------------------------- /source-data/GeoIP2-Static-IP-Score-Test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "::1.0.0.0/111" : { 4 | "score" : 0.01 5 | } 6 | }, 7 | { 8 | "::1.2.0.0/119" : { 9 | "score" : 0.02 10 | } 11 | }, 12 | { 13 | "::1.2.2.0/120" : { 14 | "score" : 0.03 15 | } 16 | }, 17 | { 18 | "::1.2.3.0/126" : { 19 | "score" : 0.04 20 | } 21 | }, 22 | { 23 | "::1.2.3.4/128" : { 24 | "score" : 0.05 25 | } 26 | }, 27 | { 28 | "::1.2.3.5/128" : { 29 | "score" : 0.06 30 | } 31 | }, 32 | { 33 | "::1.2.3.6/128" : { 34 | "score" : 0.07 35 | } 36 | }, 37 | { 38 | "::1.2.3.7/128" : { 39 | "score" : 0.08 40 | } 41 | }, 42 | { 43 | "::1.2.3.8/125" : { 44 | "score" : 0.09 45 | } 46 | }, 47 | { 48 | "::1.2.3.16/124" : { 49 | "score" : 0.1 50 | } 51 | }, 52 | { 53 | "::1.2.3.32/123" : { 54 | "score" : 0.11 55 | } 56 | }, 57 | { 58 | "::1.2.3.64/122" : { 59 | "score" : 0.12 60 | } 61 | }, 62 | { 63 | "::1.2.3.128/121" : { 64 | "score" : 0.13 65 | } 66 | }, 67 | { 68 | "::1.2.4.0/118" : { 69 | "score" : 0.14 70 | } 71 | }, 72 | { 73 | "::1.2.8.0/117" : { 74 | "score" : 0.15 75 | } 76 | }, 77 | { 78 | "::1.2.16.0/116" : { 79 | "score" : 0.16 80 | } 81 | }, 82 | { 83 | "::1.2.32.0/115" : { 84 | "score" : 0.17 85 | } 86 | }, 87 | { 88 | "::1.2.64.0/114" : { 89 | "score" : 0.18 90 | } 91 | }, 92 | { 93 | "::1.2.128.0/113" : { 94 | "score" : 0.19 95 | } 96 | }, 97 | { 98 | "::1.3.0.0/112" : { 99 | "score" : 0.2 100 | } 101 | }, 102 | { 103 | "::1.4.0.0/110" : { 104 | "score" : 0.21 105 | } 106 | }, 107 | { 108 | "::1.8.0.0/109" : { 109 | "score" : 0.22 110 | } 111 | }, 112 | { 113 | "::1.16.0.0/108" : { 114 | "score" : 0.23 115 | } 116 | }, 117 | { 118 | "::1.32.0.0/107" : { 119 | "score" : 0.24 120 | } 121 | }, 122 | { 123 | "::1.64.0.0/106" : { 124 | "score" : 0.25 125 | } 126 | }, 127 | { 128 | "::1.128.0.0/105" : { 129 | "score" : 0.26 130 | } 131 | }, 132 | { 133 | "::2.0.0.0/103" : { 134 | "score" : 0.27 135 | } 136 | }, 137 | { 138 | "::4.0.0.0/102" : { 139 | "score" : 0.28 140 | } 141 | }, 142 | { 143 | "::8.0.0.0/103" : { 144 | "score" : 0.29 145 | } 146 | }, 147 | { 148 | "::11.0.0.0/104" : { 149 | "score" : 0.3 150 | } 151 | }, 152 | { 153 | "::12.0.0.0/102" : { 154 | "score" : 0.31 155 | } 156 | }, 157 | { 158 | "::16.0.0.0/100" : { 159 | "score" : 0.32 160 | } 161 | }, 162 | { 163 | "::32.0.0.0/99" : { 164 | "score" : 0.33 165 | } 166 | }, 167 | { 168 | "::64.0.0.0/99" : { 169 | "score" : 0.34 170 | } 171 | }, 172 | { 173 | "::75.209.24.0/128" : { 174 | "score" : 0.35 175 | } 176 | }, 177 | { 178 | "::96.0.0.0/102" : { 179 | "score" : 0.36 180 | } 181 | }, 182 | { 183 | "::100.0.0.0/106" : { 184 | "score" : 0.37 185 | } 186 | }, 187 | { 188 | "::100.128.0.0/105" : { 189 | "score" : 0.38 190 | } 191 | }, 192 | { 193 | "::101.0.0.0/104" : { 194 | "score" : 0.39 195 | } 196 | }, 197 | { 198 | "::102.0.0.0/103" : { 199 | "score" : 0.4 200 | } 201 | }, 202 | { 203 | "::104.0.0.0/101" : { 204 | "score" : 0.41 205 | } 206 | }, 207 | { 208 | "::112.0.0.0/101" : { 209 | "score" : 0.42 210 | } 211 | }, 212 | { 213 | "::120.0.0.0/102" : { 214 | "score" : 0.43 215 | } 216 | }, 217 | { 218 | "::124.0.0.0/103" : { 219 | "score" : 0.44 220 | } 221 | }, 222 | { 223 | "::126.0.0.0/104" : { 224 | "score" : 0.45 225 | } 226 | }, 227 | { 228 | "::128.0.0.0/99" : { 229 | "score" : 0.46 230 | } 231 | }, 232 | { 233 | "::160.0.0.0/101" : { 234 | "score" : 0.47 235 | } 236 | }, 237 | { 238 | "::168.0.0.0/104" : { 239 | "score" : 0.48 240 | } 241 | }, 242 | { 243 | "::169.0.0.0/105" : { 244 | "score" : 0.49 245 | } 246 | }, 247 | { 248 | "::169.128.0.0/106" : { 249 | "score" : 0.5 250 | } 251 | }, 252 | { 253 | "::169.192.0.0/107" : { 254 | "score" : 0.51 255 | } 256 | }, 257 | { 258 | "::169.224.0.0/108" : { 259 | "score" : 0.52 260 | } 261 | }, 262 | { 263 | "::169.240.0.0/109" : { 264 | "score" : 0.53 265 | } 266 | }, 267 | { 268 | "::169.248.0.0/110" : { 269 | "score" : 0.54 270 | } 271 | }, 272 | { 273 | "::169.252.0.0/111" : { 274 | "score" : 0.55 275 | } 276 | }, 277 | { 278 | "::169.255.0.0/112" : { 279 | "score" : 0.56 280 | } 281 | }, 282 | { 283 | "::170.0.0.0/103" : { 284 | "score" : 0.57 285 | } 286 | }, 287 | { 288 | "::172.0.0.0/108" : { 289 | "score" : 0.58 290 | } 291 | }, 292 | { 293 | "::172.32.0.0/107" : { 294 | "score" : 0.59 295 | } 296 | }, 297 | { 298 | "::172.64.0.0/106" : { 299 | "score" : 0.6 300 | } 301 | }, 302 | { 303 | "::172.128.0.0/105" : { 304 | "score" : 0.61 305 | } 306 | }, 307 | { 308 | "::173.0.0.0/104" : { 309 | "score" : 0.62 310 | } 311 | }, 312 | { 313 | "::174.0.0.0/103" : { 314 | "score" : 0.63 315 | } 316 | }, 317 | { 318 | "::176.0.0.0/100" : { 319 | "score" : 0.64 320 | } 321 | }, 322 | { 323 | "::192.0.0.8/125" : { 324 | "score" : 0.65 325 | } 326 | }, 327 | { 328 | "::192.0.0.16/124" : { 329 | "score" : 0.66 330 | } 331 | }, 332 | { 333 | "::192.0.0.32/123" : { 334 | "score" : 0.67 335 | } 336 | }, 337 | { 338 | "::192.0.0.64/122" : { 339 | "score" : 0.68 340 | } 341 | }, 342 | { 343 | "::192.0.0.128/121" : { 344 | "score" : 0.69 345 | } 346 | }, 347 | { 348 | "::192.0.1.0/120" : { 349 | "score" : 0.7 350 | } 351 | }, 352 | { 353 | "::192.0.3.0/120" : { 354 | "score" : 0.71 355 | } 356 | }, 357 | { 358 | "::192.0.4.0/118" : { 359 | "score" : 0.72 360 | } 361 | }, 362 | { 363 | "::192.0.8.0/117" : { 364 | "score" : 0.73 365 | } 366 | }, 367 | { 368 | "::192.0.16.0/116" : { 369 | "score" : 0.74 370 | } 371 | }, 372 | { 373 | "::192.0.32.0/115" : { 374 | "score" : 0.75 375 | } 376 | }, 377 | { 378 | "::192.0.64.0/114" : { 379 | "score" : 0.76 380 | } 381 | }, 382 | { 383 | "::192.0.128.0/113" : { 384 | "score" : 0.77 385 | } 386 | }, 387 | { 388 | "::192.1.0.0/112" : { 389 | "score" : 0.78 390 | } 391 | }, 392 | { 393 | "::192.2.0.0/111" : { 394 | "score" : 0.79 395 | } 396 | }, 397 | { 398 | "::192.4.0.0/110" : { 399 | "score" : 0.8 400 | } 401 | }, 402 | { 403 | "::192.8.0.0/109" : { 404 | "score" : 0.81 405 | } 406 | }, 407 | { 408 | "::192.16.0.0/108" : { 409 | "score" : 0.82 410 | } 411 | }, 412 | { 413 | "::192.32.0.0/107" : { 414 | "score" : 0.83 415 | } 416 | }, 417 | { 418 | "::192.64.0.0/108" : { 419 | "score" : 0.84 420 | } 421 | }, 422 | { 423 | "::192.80.0.0/109" : { 424 | "score" : 0.85 425 | } 426 | }, 427 | { 428 | "::192.88.0.0/114" : { 429 | "score" : 0.86 430 | } 431 | }, 432 | { 433 | "::192.88.64.0/115" : { 434 | "score" : 0.87 435 | } 436 | }, 437 | { 438 | "::192.88.96.0/119" : { 439 | "score" : 0.88 440 | } 441 | }, 442 | { 443 | "::192.88.98.0/120" : { 444 | "score" : 0.89 445 | } 446 | }, 447 | { 448 | "::192.88.100.0/118" : { 449 | "score" : 0.9 450 | } 451 | }, 452 | { 453 | "::192.88.104.0/117" : { 454 | "score" : 0.91 455 | } 456 | }, 457 | { 458 | "::192.88.112.0/116" : { 459 | "score" : 0.92 460 | } 461 | }, 462 | { 463 | "::192.88.128.0/113" : { 464 | "score" : 0.93 465 | } 466 | }, 467 | { 468 | "::192.89.0.0/112" : { 469 | "score" : 0.94 470 | } 471 | }, 472 | { 473 | "::192.90.0.0/111" : { 474 | "score" : 0.95 475 | } 476 | }, 477 | { 478 | "::192.92.0.0/110" : { 479 | "score" : 0.96 480 | } 481 | }, 482 | { 483 | "::192.96.0.0/107" : { 484 | "score" : 0.97 485 | } 486 | }, 487 | { 488 | "::192.128.0.0/107" : { 489 | "score" : 0.98 490 | } 491 | }, 492 | { 493 | "::192.160.0.0/109" : { 494 | "score" : 0.99 495 | } 496 | }, 497 | { 498 | "::192.169.0.0/112" : { 499 | "score" : 1.0 500 | } 501 | }, 502 | { 503 | "::192.170.0.0/111" : { 504 | "score" : 1.01 505 | } 506 | }, 507 | { 508 | "::192.172.0.0/110" : { 509 | "score" : 1.02 510 | } 511 | }, 512 | { 513 | "::192.176.0.0/108" : { 514 | "score" : 1.03 515 | } 516 | }, 517 | { 518 | "::192.192.0.0/106" : { 519 | "score" : 1.04 520 | } 521 | }, 522 | { 523 | "::193.0.0.0/104" : { 524 | "score" : 1.05 525 | } 526 | }, 527 | { 528 | "::194.0.0.0/103" : { 529 | "score" : 1.06 530 | } 531 | }, 532 | { 533 | "::196.0.0.0/103" : { 534 | "score" : 1.07 535 | } 536 | }, 537 | { 538 | "::198.0.0.0/108" : { 539 | "score" : 1.08 540 | } 541 | }, 542 | { 543 | "::198.16.0.0/111" : { 544 | "score" : 1.09 545 | } 546 | }, 547 | { 548 | "::198.20.0.0/110" : { 549 | "score" : 1.1 550 | } 551 | }, 552 | { 553 | "::198.24.0.0/109" : { 554 | "score" : 1.11 555 | } 556 | }, 557 | { 558 | "::198.32.0.0/108" : { 559 | "score" : 1.12 560 | } 561 | }, 562 | { 563 | "::198.48.0.0/111" : { 564 | "score" : 1.13 565 | } 566 | }, 567 | { 568 | "::198.50.0.0/112" : { 569 | "score" : 1.14 570 | } 571 | }, 572 | { 573 | "::198.51.0.0/114" : { 574 | "score" : 1.15 575 | } 576 | }, 577 | { 578 | "::198.51.64.0/115" : { 579 | "score" : 1.16 580 | } 581 | }, 582 | { 583 | "::198.51.96.0/118" : { 584 | "score" : 1.17 585 | } 586 | }, 587 | { 588 | "::198.51.101.0/120" : { 589 | "score" : 1.18 590 | } 591 | }, 592 | { 593 | "::198.51.102.0/119" : { 594 | "score" : 1.19 595 | } 596 | }, 597 | { 598 | "::198.51.104.0/117" : { 599 | "score" : 1.2 600 | } 601 | }, 602 | { 603 | "::198.51.112.0/116" : { 604 | "score" : 1.21 605 | } 606 | }, 607 | { 608 | "::198.51.128.0/113" : { 609 | "score" : 1.22 610 | } 611 | }, 612 | { 613 | "::198.52.0.0/110" : { 614 | "score" : 1.23 615 | } 616 | }, 617 | { 618 | "::198.56.0.0/109" : { 619 | "score" : 1.24 620 | } 621 | }, 622 | { 623 | "::198.64.0.0/106" : { 624 | "score" : 1.25 625 | } 626 | }, 627 | { 628 | "::198.128.0.0/105" : { 629 | "score" : 1.26 630 | } 631 | }, 632 | { 633 | "::199.0.0.0/104" : { 634 | "score" : 1.27 635 | } 636 | }, 637 | { 638 | "::200.0.0.0/103" : { 639 | "score" : 1.28 640 | } 641 | }, 642 | { 643 | "::202.0.0.0/104" : { 644 | "score" : 1.29 645 | } 646 | }, 647 | { 648 | "::203.0.0.0/114" : { 649 | "score" : 1.3 650 | } 651 | }, 652 | { 653 | "::203.0.64.0/115" : { 654 | "score" : 1.31 655 | } 656 | }, 657 | { 658 | "::203.0.96.0/116" : { 659 | "score" : 1.32 660 | } 661 | }, 662 | { 663 | "::203.0.112.0/120" : { 664 | "score" : 1.33 665 | } 666 | }, 667 | { 668 | "::203.0.114.0/119" : { 669 | "score" : 1.34 670 | } 671 | }, 672 | { 673 | "::203.0.116.0/118" : { 674 | "score" : 1.35 675 | } 676 | }, 677 | { 678 | "::203.0.120.0/117" : { 679 | "score" : 1.36 680 | } 681 | }, 682 | { 683 | "::203.0.128.0/113" : { 684 | "score" : 1.37 685 | } 686 | }, 687 | { 688 | "::203.1.0.0/112" : { 689 | "score" : 1.38 690 | } 691 | }, 692 | { 693 | "::203.2.0.0/111" : { 694 | "score" : 1.39 695 | } 696 | }, 697 | { 698 | "::203.4.0.0/110" : { 699 | "score" : 1.4 700 | } 701 | }, 702 | { 703 | "::203.8.0.0/109" : { 704 | "score" : 1.41 705 | } 706 | }, 707 | { 708 | "::203.16.0.0/108" : { 709 | "score" : 1.42 710 | } 711 | }, 712 | { 713 | "::203.32.0.0/107" : { 714 | "score" : 1.43 715 | } 716 | }, 717 | { 718 | "::203.64.0.0/106" : { 719 | "score" : 1.44 720 | } 721 | }, 722 | { 723 | "::203.128.0.0/105" : { 724 | "score" : 1.45 725 | } 726 | }, 727 | { 728 | "::204.0.0.0/102" : { 729 | "score" : 1.46 730 | } 731 | }, 732 | { 733 | "::208.0.0.0/100" : { 734 | "score" : 1.47 735 | } 736 | }, 737 | { 738 | "::214.2.3.0/126" : { 739 | "score" : 0.04 740 | } 741 | }, 742 | { 743 | "::214.2.3.4/128" : { 744 | "score" : 0.05 745 | } 746 | }, 747 | { 748 | "::214.2.3.5/128" : { 749 | "score" : 0.06 750 | } 751 | }, 752 | { 753 | "::1:0:0:0/80" : { 754 | "score" : 1.48 755 | } 756 | }, 757 | { 758 | "::2:0:0:0/79" : { 759 | "score" : 1.49 760 | } 761 | }, 762 | { 763 | "::4:0:0:0/78" : { 764 | "score" : 1.5 765 | } 766 | }, 767 | { 768 | "::8:0:0:0/77" : { 769 | "score" : 1.51 770 | } 771 | }, 772 | { 773 | "::10:0:0:0/76" : { 774 | "score" : 1.52 775 | } 776 | }, 777 | { 778 | "::20:0:0:0/75" : { 779 | "score" : 1.53 780 | } 781 | }, 782 | { 783 | "::40:0:0:0/74" : { 784 | "score" : 1.54 785 | } 786 | }, 787 | { 788 | "::80:0:0:0/73" : { 789 | "score" : 1.55 790 | } 791 | }, 792 | { 793 | "::100:0:0:0/72" : { 794 | "score" : 1.56 795 | } 796 | }, 797 | { 798 | "::200:0:0:0/71" : { 799 | "score" : 1.57 800 | } 801 | }, 802 | { 803 | "::400:0:0:0/70" : { 804 | "score" : 1.58 805 | } 806 | }, 807 | { 808 | "::800:0:0:0/69" : { 809 | "score" : 1.59 810 | } 811 | }, 812 | { 813 | "::1000:0:0:0/68" : { 814 | "score" : 1.6 815 | } 816 | }, 817 | { 818 | "::2000:0:0:0/67" : { 819 | "score" : 1.61 820 | } 821 | }, 822 | { 823 | "::4000:0:0:0/66" : { 824 | "score" : 1.62 825 | } 826 | }, 827 | { 828 | "::8000:0:0:0/65" : { 829 | "score" : 1.63 830 | } 831 | }, 832 | { 833 | "0:0:0:1::/64" : { 834 | "score" : 1.64 835 | } 836 | }, 837 | { 838 | "0:0:0:2::/63" : { 839 | "score" : 1.65 840 | } 841 | }, 842 | { 843 | "0:0:0:4::/62" : { 844 | "score" : 1.66 845 | } 846 | }, 847 | { 848 | "0:0:0:8::/61" : { 849 | "score" : 1.67 850 | } 851 | }, 852 | { 853 | "0:0:0:10::/60" : { 854 | "score" : 1.68 855 | } 856 | }, 857 | { 858 | "0:0:0:20::/59" : { 859 | "score" : 1.69 860 | } 861 | }, 862 | { 863 | "0:0:0:40::/58" : { 864 | "score" : 1.7 865 | } 866 | }, 867 | { 868 | "0:0:0:80::/57" : { 869 | "score" : 1.71 870 | } 871 | }, 872 | { 873 | "0:0:0:100::/56" : { 874 | "score" : 1.72 875 | } 876 | }, 877 | { 878 | "0:0:0:200::/55" : { 879 | "score" : 1.73 880 | } 881 | }, 882 | { 883 | "0:0:0:400::/54" : { 884 | "score" : 1.74 885 | } 886 | }, 887 | { 888 | "0:0:0:800::/53" : { 889 | "score" : 1.75 890 | } 891 | }, 892 | { 893 | "0:0:0:1000::/52" : { 894 | "score" : 1.76 895 | } 896 | }, 897 | { 898 | "0:0:0:2000::/51" : { 899 | "score" : 1.77 900 | } 901 | }, 902 | { 903 | "0:0:0:4000::/50" : { 904 | "score" : 1.78 905 | } 906 | }, 907 | { 908 | "0:0:0:8000::/49" : { 909 | "score" : 1.79 910 | } 911 | }, 912 | { 913 | "0:0:1::/48" : { 914 | "score" : 1.8 915 | } 916 | }, 917 | { 918 | "0:0:2::/47" : { 919 | "score" : 1.81 920 | } 921 | }, 922 | { 923 | "0:0:4::/46" : { 924 | "score" : 1.82 925 | } 926 | }, 927 | { 928 | "0:0:8::/45" : { 929 | "score" : 1.83 930 | } 931 | }, 932 | { 933 | "0:0:10::/44" : { 934 | "score" : 1.84 935 | } 936 | }, 937 | { 938 | "0:0:20::/43" : { 939 | "score" : 1.85 940 | } 941 | }, 942 | { 943 | "0:0:40::/42" : { 944 | "score" : 1.86 945 | } 946 | }, 947 | { 948 | "0:0:80::/41" : { 949 | "score" : 1.87 950 | } 951 | }, 952 | { 953 | "0:0:100::/40" : { 954 | "score" : 1.88 955 | } 956 | }, 957 | { 958 | "0:0:200::/39" : { 959 | "score" : 1.89 960 | } 961 | }, 962 | { 963 | "0:0:400::/38" : { 964 | "score" : 1.9 965 | } 966 | }, 967 | { 968 | "0:0:800::/37" : { 969 | "score" : 1.91 970 | } 971 | }, 972 | { 973 | "0:0:1000::/36" : { 974 | "score" : 1.92 975 | } 976 | }, 977 | { 978 | "0:0:2000::/35" : { 979 | "score" : 1.93 980 | } 981 | }, 982 | { 983 | "0:0:4000::/34" : { 984 | "score" : 1.94 985 | } 986 | }, 987 | { 988 | "0:0:8000::/33" : { 989 | "score" : 1.95 990 | } 991 | }, 992 | { 993 | "0:1::/32" : { 994 | "score" : 1.96 995 | } 996 | }, 997 | { 998 | "0:2::/31" : { 999 | "score" : 1.97 1000 | } 1001 | }, 1002 | { 1003 | "0:4::/30" : { 1004 | "score" : 1.98 1005 | } 1006 | }, 1007 | { 1008 | "0:8::/29" : { 1009 | "score" : 1.99 1010 | } 1011 | }, 1012 | { 1013 | "0:10::/28" : { 1014 | "score" : 2.0 1015 | } 1016 | }, 1017 | { 1018 | "0:20::/27" : { 1019 | "score" : 2.01 1020 | } 1021 | }, 1022 | { 1023 | "0:40::/26" : { 1024 | "score" : 2.02 1025 | } 1026 | }, 1027 | { 1028 | "0:80::/25" : { 1029 | "score" : 2.03 1030 | } 1031 | }, 1032 | { 1033 | "0:100::/24" : { 1034 | "score" : 2.04 1035 | } 1036 | }, 1037 | { 1038 | "0:200::/23" : { 1039 | "score" : 2.05 1040 | } 1041 | }, 1042 | { 1043 | "0:400::/22" : { 1044 | "score" : 2.06 1045 | } 1046 | }, 1047 | { 1048 | "0:800::/21" : { 1049 | "score" : 2.07 1050 | } 1051 | }, 1052 | { 1053 | "0:1000::/20" : { 1054 | "score" : 2.08 1055 | } 1056 | }, 1057 | { 1058 | "0:2000::/19" : { 1059 | "score" : 2.09 1060 | } 1061 | }, 1062 | { 1063 | "0:4000::/18" : { 1064 | "score" : 2.1 1065 | } 1066 | }, 1067 | { 1068 | "0:8000::/17" : { 1069 | "score" : 2.11 1070 | } 1071 | }, 1072 | { 1073 | "1::/16" : { 1074 | "score" : 2.12 1075 | } 1076 | }, 1077 | { 1078 | "2::/15" : { 1079 | "score" : 2.13 1080 | } 1081 | }, 1082 | { 1083 | "4::/14" : { 1084 | "score" : 2.14 1085 | } 1086 | }, 1087 | { 1088 | "8::/13" : { 1089 | "score" : 2.15 1090 | } 1091 | }, 1092 | { 1093 | "10::/12" : { 1094 | "score" : 2.16 1095 | } 1096 | }, 1097 | { 1098 | "20::/11" : { 1099 | "score" : 2.17 1100 | } 1101 | }, 1102 | { 1103 | "40::/10" : { 1104 | "score" : 2.18 1105 | } 1106 | }, 1107 | { 1108 | "80::/9" : { 1109 | "score" : 2.19 1110 | } 1111 | }, 1112 | { 1113 | "100:0:0:1::/64" : { 1114 | "score" : 2.2 1115 | } 1116 | }, 1117 | { 1118 | "100:0:0:2::/63" : { 1119 | "score" : 2.21 1120 | } 1121 | }, 1122 | { 1123 | "100:0:0:4::/62" : { 1124 | "score" : 2.22 1125 | } 1126 | }, 1127 | { 1128 | "100:0:0:8::/61" : { 1129 | "score" : 2.23 1130 | } 1131 | }, 1132 | { 1133 | "100:0:0:10::/60" : { 1134 | "score" : 2.24 1135 | } 1136 | }, 1137 | { 1138 | "100:0:0:20::/59" : { 1139 | "score" : 2.25 1140 | } 1141 | }, 1142 | { 1143 | "100:0:0:40::/58" : { 1144 | "score" : 2.26 1145 | } 1146 | }, 1147 | { 1148 | "100:0:0:80::/57" : { 1149 | "score" : 2.27 1150 | } 1151 | }, 1152 | { 1153 | "100:0:0:100::/56" : { 1154 | "score" : 2.28 1155 | } 1156 | }, 1157 | { 1158 | "100:0:0:200::/55" : { 1159 | "score" : 2.29 1160 | } 1161 | }, 1162 | { 1163 | "100:0:0:400::/54" : { 1164 | "score" : 2.3 1165 | } 1166 | }, 1167 | { 1168 | "100:0:0:800::/53" : { 1169 | "score" : 2.31 1170 | } 1171 | }, 1172 | { 1173 | "100:0:0:1000::/52" : { 1174 | "score" : 2.32 1175 | } 1176 | }, 1177 | { 1178 | "100:0:0:2000::/51" : { 1179 | "score" : 2.33 1180 | } 1181 | }, 1182 | { 1183 | "100:0:0:4000::/50" : { 1184 | "score" : 2.34 1185 | } 1186 | }, 1187 | { 1188 | "100:0:0:8000::/49" : { 1189 | "score" : 2.35 1190 | } 1191 | }, 1192 | { 1193 | "100:0:1::/48" : { 1194 | "score" : 2.36 1195 | } 1196 | }, 1197 | { 1198 | "100:0:2::/47" : { 1199 | "score" : 2.37 1200 | } 1201 | }, 1202 | { 1203 | "100:0:4::/46" : { 1204 | "score" : 2.38 1205 | } 1206 | }, 1207 | { 1208 | "100:0:8::/45" : { 1209 | "score" : 2.39 1210 | } 1211 | }, 1212 | { 1213 | "100:0:10::/44" : { 1214 | "score" : 2.4 1215 | } 1216 | }, 1217 | { 1218 | "100:0:20::/43" : { 1219 | "score" : 2.41 1220 | } 1221 | }, 1222 | { 1223 | "100:0:40::/42" : { 1224 | "score" : 2.42 1225 | } 1226 | }, 1227 | { 1228 | "100:0:80::/41" : { 1229 | "score" : 2.43 1230 | } 1231 | }, 1232 | { 1233 | "100:0:100::/40" : { 1234 | "score" : 2.44 1235 | } 1236 | }, 1237 | { 1238 | "100:0:200::/39" : { 1239 | "score" : 2.45 1240 | } 1241 | }, 1242 | { 1243 | "100:0:400::/38" : { 1244 | "score" : 2.46 1245 | } 1246 | }, 1247 | { 1248 | "100:0:800::/37" : { 1249 | "score" : 2.47 1250 | } 1251 | }, 1252 | { 1253 | "100:0:1000::/36" : { 1254 | "score" : 2.48 1255 | } 1256 | }, 1257 | { 1258 | "100:0:2000::/35" : { 1259 | "score" : 2.49 1260 | } 1261 | }, 1262 | { 1263 | "100:0:4000::/34" : { 1264 | "score" : 2.5 1265 | } 1266 | }, 1267 | { 1268 | "100:0:8000::/33" : { 1269 | "score" : 2.51 1270 | } 1271 | }, 1272 | { 1273 | "100:1::/32" : { 1274 | "score" : 2.52 1275 | } 1276 | }, 1277 | { 1278 | "100:2::/31" : { 1279 | "score" : 2.53 1280 | } 1281 | }, 1282 | { 1283 | "100:4::/30" : { 1284 | "score" : 2.54 1285 | } 1286 | }, 1287 | { 1288 | "100:8::/29" : { 1289 | "score" : 2.55 1290 | } 1291 | }, 1292 | { 1293 | "100:10::/28" : { 1294 | "score" : 2.56 1295 | } 1296 | }, 1297 | { 1298 | "100:20::/27" : { 1299 | "score" : 2.57 1300 | } 1301 | }, 1302 | { 1303 | "100:40::/26" : { 1304 | "score" : 2.58 1305 | } 1306 | }, 1307 | { 1308 | "100:80::/25" : { 1309 | "score" : 2.59 1310 | } 1311 | }, 1312 | { 1313 | "100:100::/24" : { 1314 | "score" : 2.6 1315 | } 1316 | }, 1317 | { 1318 | "100:200::/23" : { 1319 | "score" : 2.61 1320 | } 1321 | }, 1322 | { 1323 | "100:400::/22" : { 1324 | "score" : 2.62 1325 | } 1326 | }, 1327 | { 1328 | "100:800::/21" : { 1329 | "score" : 2.63 1330 | } 1331 | }, 1332 | { 1333 | "100:1000::/20" : { 1334 | "score" : 2.64 1335 | } 1336 | }, 1337 | { 1338 | "100:2000::/19" : { 1339 | "score" : 2.65 1340 | } 1341 | }, 1342 | { 1343 | "100:4000::/18" : { 1344 | "score" : 2.66 1345 | } 1346 | }, 1347 | { 1348 | "100:8000::/17" : { 1349 | "score" : 2.67 1350 | } 1351 | }, 1352 | { 1353 | "101::/16" : { 1354 | "score" : 2.68 1355 | } 1356 | }, 1357 | { 1358 | "102::/15" : { 1359 | "score" : 2.69 1360 | } 1361 | }, 1362 | { 1363 | "104::/14" : { 1364 | "score" : 2.7 1365 | } 1366 | }, 1367 | { 1368 | "108::/13" : { 1369 | "score" : 2.71 1370 | } 1371 | }, 1372 | { 1373 | "110::/12" : { 1374 | "score" : 2.72 1375 | } 1376 | }, 1377 | { 1378 | "120::/11" : { 1379 | "score" : 2.73 1380 | } 1381 | }, 1382 | { 1383 | "140::/10" : { 1384 | "score" : 2.74 1385 | } 1386 | }, 1387 | { 1388 | "180::/9" : { 1389 | "score" : 2.75 1390 | } 1391 | }, 1392 | { 1393 | "200::/7" : { 1394 | "score" : 2.76 1395 | } 1396 | }, 1397 | { 1398 | "400::/6" : { 1399 | "score" : 2.77 1400 | } 1401 | }, 1402 | { 1403 | "800::/5" : { 1404 | "score" : 2.78 1405 | } 1406 | }, 1407 | { 1408 | "1000::/4" : { 1409 | "score" : 2.79 1410 | } 1411 | }, 1412 | { 1413 | "2000::/16" : { 1414 | "score" : 2.8 1415 | } 1416 | }, 1417 | { 1418 | "2001:200::/23" : { 1419 | "score" : 2.81 1420 | } 1421 | }, 1422 | { 1423 | "2001:220::/128" : { 1424 | "score" : 2.82 1425 | } 1426 | }, 1427 | { 1428 | "2001:400::/22" : { 1429 | "score" : 2.83 1430 | } 1431 | }, 1432 | { 1433 | "2001:800::/22" : { 1434 | "score" : 2.84 1435 | } 1436 | }, 1437 | { 1438 | "2001:c00::/24" : { 1439 | "score" : 2.85 1440 | } 1441 | }, 1442 | { 1443 | "2001:d00::/25" : { 1444 | "score" : 2.86 1445 | } 1446 | }, 1447 | { 1448 | "2001:d80::/27" : { 1449 | "score" : 2.87 1450 | } 1451 | }, 1452 | { 1453 | "2001:da0::/28" : { 1454 | "score" : 2.88 1455 | } 1456 | }, 1457 | { 1458 | "2001:db0::/29" : { 1459 | "score" : 2.89 1460 | } 1461 | }, 1462 | { 1463 | "2001:db9::/32" : { 1464 | "score" : 2.9 1465 | } 1466 | }, 1467 | { 1468 | "2001:dba::/31" : { 1469 | "score" : 2.91 1470 | } 1471 | }, 1472 | { 1473 | "2001:dbc::/30" : { 1474 | "score" : 2.92 1475 | } 1476 | }, 1477 | { 1478 | "2001:dc0::/26" : { 1479 | "score" : 2.93 1480 | } 1481 | }, 1482 | { 1483 | "2001:e00::/23" : { 1484 | "score" : 2.94 1485 | } 1486 | }, 1487 | { 1488 | "2001:1000::/20" : { 1489 | "score" : 2.95 1490 | } 1491 | }, 1492 | { 1493 | "2001:2000::/19" : { 1494 | "score" : 2.96 1495 | } 1496 | }, 1497 | { 1498 | "2001:4000::/18" : { 1499 | "score" : 2.97 1500 | } 1501 | }, 1502 | { 1503 | "2001:8000::/18" : { 1504 | "score" : 2.98 1505 | } 1506 | }, 1507 | { 1508 | "2001:c000::/19" : { 1509 | "score" : 2.99 1510 | } 1511 | }, 1512 | { 1513 | "2001:e000::/21" : { 1514 | "score" : 3.0 1515 | } 1516 | }, 1517 | { 1518 | "2001:e800::/22" : { 1519 | "score" : 3.01 1520 | } 1521 | }, 1522 | { 1523 | "2001:ec00::/24" : { 1524 | "score" : 3.02 1525 | } 1526 | }, 1527 | { 1528 | "2001:ed00::/25" : { 1529 | "score" : 3.03 1530 | } 1531 | }, 1532 | { 1533 | "2001:ed80::/27" : { 1534 | "score" : 3.04 1535 | } 1536 | }, 1537 | { 1538 | "2001:eda0::/28" : { 1539 | "score" : 3.05 1540 | } 1541 | }, 1542 | { 1543 | "2001:edb0::/29" : { 1544 | "score" : 3.06 1545 | } 1546 | }, 1547 | { 1548 | "2001:edb8::/48" : { 1549 | "score" : 3.07 1550 | } 1551 | }, 1552 | { 1553 | "2001:edb8:1::/64" : { 1554 | "score" : 3.08 1555 | } 1556 | }, 1557 | { 1558 | "2001:edb8:1:1::/64" : { 1559 | "score" : 3.09 1560 | } 1561 | }, 1562 | { 1563 | "2001:edb8:1:2::/63" : { 1564 | "score" : 3.1 1565 | } 1566 | }, 1567 | { 1568 | "2001:edb8:1:4::/62" : { 1569 | "score" : 3.11 1570 | } 1571 | }, 1572 | { 1573 | "2001:edb8:1:8::/61" : { 1574 | "score" : 3.12 1575 | } 1576 | }, 1577 | { 1578 | "2001:edb8:1:10::/60" : { 1579 | "score" : 3.13 1580 | } 1581 | }, 1582 | { 1583 | "2001:edb8:1:20::/59" : { 1584 | "score" : 3.14 1585 | } 1586 | }, 1587 | { 1588 | "2001:edb8:1:40::/58" : { 1589 | "score" : 3.15 1590 | } 1591 | }, 1592 | { 1593 | "2001:edb8:1:80::/57" : { 1594 | "score" : 3.16 1595 | } 1596 | }, 1597 | { 1598 | "2001:edb8:1:100::/56" : { 1599 | "score" : 3.17 1600 | } 1601 | }, 1602 | { 1603 | "2001:edb8:1:200::/55" : { 1604 | "score" : 3.18 1605 | } 1606 | }, 1607 | { 1608 | "2001:edb8:1:400::/54" : { 1609 | "score" : 3.19 1610 | } 1611 | }, 1612 | { 1613 | "2001:edb8:1:800::/53" : { 1614 | "score" : 3.2 1615 | } 1616 | }, 1617 | { 1618 | "2001:edb8:1:1000::/52" : { 1619 | "score" : 3.21 1620 | } 1621 | }, 1622 | { 1623 | "2001:edb8:1:2000::/51" : { 1624 | "score" : 3.22 1625 | } 1626 | }, 1627 | { 1628 | "2001:edb8:1:4000::/50" : { 1629 | "score" : 3.23 1630 | } 1631 | }, 1632 | { 1633 | "2001:edb8:1:8000::/49" : { 1634 | "score" : 3.24 1635 | } 1636 | }, 1637 | { 1638 | "2001:edb8:2::/47" : { 1639 | "score" : 3.25 1640 | } 1641 | }, 1642 | { 1643 | "2001:edb8:4::/46" : { 1644 | "score" : 3.26 1645 | } 1646 | }, 1647 | { 1648 | "2001:edb8:8::/45" : { 1649 | "score" : 3.27 1650 | } 1651 | }, 1652 | { 1653 | "2001:edb8:10::/44" : { 1654 | "score" : 3.28 1655 | } 1656 | }, 1657 | { 1658 | "2001:edb8:20::/43" : { 1659 | "score" : 3.29 1660 | } 1661 | }, 1662 | { 1663 | "2001:edb8:40::/42" : { 1664 | "score" : 3.3 1665 | } 1666 | }, 1667 | { 1668 | "2001:edb8:80::/41" : { 1669 | "score" : 3.31 1670 | } 1671 | }, 1672 | { 1673 | "2001:edb8:100::/40" : { 1674 | "score" : 3.32 1675 | } 1676 | }, 1677 | { 1678 | "2001:edb8:200::/39" : { 1679 | "score" : 3.33 1680 | } 1681 | }, 1682 | { 1683 | "2001:edb8:400::/38" : { 1684 | "score" : 3.34 1685 | } 1686 | }, 1687 | { 1688 | "2001:edb8:800::/37" : { 1689 | "score" : 3.35 1690 | } 1691 | }, 1692 | { 1693 | "2001:edb8:1000::/36" : { 1694 | "score" : 3.36 1695 | } 1696 | }, 1697 | { 1698 | "2001:edb8:2000::/35" : { 1699 | "score" : 3.37 1700 | } 1701 | }, 1702 | { 1703 | "2001:edb8:4000::/34" : { 1704 | "score" : 3.38 1705 | } 1706 | }, 1707 | { 1708 | "2001:edb8:8000::/38" : { 1709 | "score" : 3.39 1710 | } 1711 | }, 1712 | { 1713 | "2001:edb8:8400::/40" : { 1714 | "score" : 3.4 1715 | } 1716 | }, 1717 | { 1718 | "2001:edb8:8500::/41" : { 1719 | "score" : 3.41 1720 | } 1721 | }, 1722 | { 1723 | "2001:edb8:8580::/43" : { 1724 | "score" : 3.42 1725 | } 1726 | }, 1727 | { 1728 | "2001:edb8:85a0::/47" : { 1729 | "score" : 3.43 1730 | } 1731 | }, 1732 | { 1733 | "2001:edb8:85a2::/48" : { 1734 | "score" : 3.44 1735 | } 1736 | }, 1737 | { 1738 | "2001:edb8:85a3::/64" : { 1739 | "score" : 3.45 1740 | } 1741 | }, 1742 | { 1743 | "2001:edb8:85a3:1::/64" : { 1744 | "score" : 3.46 1745 | } 1746 | }, 1747 | { 1748 | "2001:edb8:85a3:2::/63" : { 1749 | "score" : 3.47 1750 | } 1751 | }, 1752 | { 1753 | "2001:edb8:85a3:4::/62" : { 1754 | "score" : 3.48 1755 | } 1756 | }, 1757 | { 1758 | "2001:edb8:85a3:8::/61" : { 1759 | "score" : 3.49 1760 | } 1761 | }, 1762 | { 1763 | "2001:edb8:85a3:10::/60" : { 1764 | "score" : 3.5 1765 | } 1766 | }, 1767 | { 1768 | "2001:edb8:85a3:20::/59" : { 1769 | "score" : 3.51 1770 | } 1771 | }, 1772 | { 1773 | "2001:edb8:85a3:40::/58" : { 1774 | "score" : 3.52 1775 | } 1776 | }, 1777 | { 1778 | "2001:edb8:85a3:80::/57" : { 1779 | "score" : 3.53 1780 | } 1781 | }, 1782 | { 1783 | "2001:edb8:85a3:100::/56" : { 1784 | "score" : 3.54 1785 | } 1786 | }, 1787 | { 1788 | "2001:edb8:85a3:200::/55" : { 1789 | "score" : 3.55 1790 | } 1791 | }, 1792 | { 1793 | "2001:edb8:85a3:400::/54" : { 1794 | "score" : 3.56 1795 | } 1796 | }, 1797 | { 1798 | "2001:edb8:85a3:800::/53" : { 1799 | "score" : 3.57 1800 | } 1801 | }, 1802 | { 1803 | "2001:edb8:85a3:1000::/52" : { 1804 | "score" : 3.58 1805 | } 1806 | }, 1807 | { 1808 | "2001:edb8:85a3:2000::/51" : { 1809 | "score" : 3.59 1810 | } 1811 | }, 1812 | { 1813 | "2001:edb8:85a3:4000::/50" : { 1814 | "score" : 3.6 1815 | } 1816 | }, 1817 | { 1818 | "2001:edb8:85a3:8000::/49" : { 1819 | "score" : 3.61 1820 | } 1821 | }, 1822 | { 1823 | "2001:edb8:85a4::/46" : { 1824 | "score" : 3.62 1825 | } 1826 | }, 1827 | { 1828 | "2001:edb8:85a8::/45" : { 1829 | "score" : 3.63 1830 | } 1831 | }, 1832 | { 1833 | "2001:edb8:85b0::/44" : { 1834 | "score" : 3.64 1835 | } 1836 | }, 1837 | { 1838 | "2001:edb8:85c0::/42" : { 1839 | "score" : 3.65 1840 | } 1841 | }, 1842 | { 1843 | "2001:edb8:8600::/39" : { 1844 | "score" : 3.66 1845 | } 1846 | }, 1847 | { 1848 | "2001:edb8:8800::/37" : { 1849 | "score" : 3.67 1850 | } 1851 | }, 1852 | { 1853 | "2001:edb8:9000::/36" : { 1854 | "score" : 3.68 1855 | } 1856 | }, 1857 | { 1858 | "2001:edb8:a000::/35" : { 1859 | "score" : 3.69 1860 | } 1861 | }, 1862 | { 1863 | "2001:edb8:c000::/36" : { 1864 | "score" : 3.7 1865 | } 1866 | }, 1867 | { 1868 | "2001:edb8:d000::/37" : { 1869 | "score" : 3.71 1870 | } 1871 | }, 1872 | { 1873 | "2001:edb8:d800::/38" : { 1874 | "score" : 3.72 1875 | } 1876 | }, 1877 | { 1878 | "2001:edb8:dc00::/39" : { 1879 | "score" : 3.73 1880 | } 1881 | }, 1882 | { 1883 | "2001:edb8:de00::/41" : { 1884 | "score" : 3.74 1885 | } 1886 | }, 1887 | { 1888 | "2001:edb8:de80::/43" : { 1889 | "score" : 3.75 1890 | } 1891 | }, 1892 | { 1893 | "2001:edb8:dea0::/45" : { 1894 | "score" : 3.76 1895 | } 1896 | }, 1897 | { 1898 | "2001:edb8:dea8::/46" : { 1899 | "score" : 3.77 1900 | } 1901 | }, 1902 | { 1903 | "2001:edb8:deac::/48" : { 1904 | "score" : 3.78 1905 | } 1906 | }, 1907 | { 1908 | "2001:edb8:dead::/49" : { 1909 | "score" : 3.79 1910 | } 1911 | }, 1912 | { 1913 | "2001:edb8:dead:8000::/50" : { 1914 | "score" : 3.8 1915 | } 1916 | }, 1917 | { 1918 | "2001:edb8:dead:c000::/52" : { 1919 | "score" : 3.81 1920 | } 1921 | }, 1922 | { 1923 | "2001:edb8:dead:d000::/53" : { 1924 | "score" : 3.82 1925 | } 1926 | }, 1927 | { 1928 | "2001:edb8:dead:d800::/54" : { 1929 | "score" : 3.83 1930 | } 1931 | }, 1932 | { 1933 | "2001:edb8:dead:dc00::/55" : { 1934 | "score" : 3.84 1935 | } 1936 | }, 1937 | { 1938 | "2001:edb8:dead:de00::/57" : { 1939 | "score" : 3.85 1940 | } 1941 | }, 1942 | { 1943 | "2001:edb8:dead:de80::/59" : { 1944 | "score" : 3.86 1945 | } 1946 | }, 1947 | { 1948 | "2001:edb8:dead:dea0::/61" : { 1949 | "score" : 3.87 1950 | } 1951 | }, 1952 | { 1953 | "2001:edb8:dead:dea8::/62" : { 1954 | "score" : 3.88 1955 | } 1956 | }, 1957 | { 1958 | "2001:edb8:dead:deac::/64" : { 1959 | "score" : 3.89 1960 | } 1961 | }, 1962 | { 1963 | "2001:edb8:dead:dead::/64" : { 1964 | "score" : 3.9 1965 | } 1966 | }, 1967 | { 1968 | "2001:edb8:dead:deae::/63" : { 1969 | "score" : 3.91 1970 | } 1971 | }, 1972 | { 1973 | "2001:edb8:dead:deb0::/60" : { 1974 | "score" : 3.92 1975 | } 1976 | }, 1977 | { 1978 | "2001:edb8:dead:dec0::/58" : { 1979 | "score" : 3.93 1980 | } 1981 | }, 1982 | { 1983 | "2001:edb8:dead:df00::/56" : { 1984 | "score" : 3.94 1985 | } 1986 | }, 1987 | { 1988 | "2001:edb8:dead:e000::/51" : { 1989 | "score" : 3.95 1990 | } 1991 | }, 1992 | { 1993 | "2001:edb8:deae::/47" : { 1994 | "score" : 3.96 1995 | } 1996 | }, 1997 | { 1998 | "2001:edb8:deb0::/44" : { 1999 | "score" : 3.97 2000 | } 2001 | }, 2002 | { 2003 | "2001:edb8:dec0::/42" : { 2004 | "score" : 3.98 2005 | } 2006 | }, 2007 | { 2008 | "2001:edb8:df00::/40" : { 2009 | "score" : 3.99 2010 | } 2011 | }, 2012 | { 2013 | "2001:edb8:e000::/35" : { 2014 | "score" : 4.0 2015 | } 2016 | }, 2017 | { 2018 | "2001:edb9::/32" : { 2019 | "score" : 4.01 2020 | } 2021 | }, 2022 | { 2023 | "2001:edba::/31" : { 2024 | "score" : 4.02 2025 | } 2026 | }, 2027 | { 2028 | "2001:edbc::/30" : { 2029 | "score" : 4.03 2030 | } 2031 | }, 2032 | { 2033 | "2001:edc0::/26" : { 2034 | "score" : 4.04 2035 | } 2036 | }, 2037 | { 2038 | "2001:ee00::/23" : { 2039 | "score" : 4.05 2040 | } 2041 | }, 2042 | { 2043 | "2001:f000::/20" : { 2044 | "score" : 4.06 2045 | } 2046 | }, 2047 | { 2048 | "2003::/16" : { 2049 | "score" : 4.07 2050 | } 2051 | }, 2052 | { 2053 | "2004::/14" : { 2054 | "score" : 4.08 2055 | } 2056 | }, 2057 | { 2058 | "2008::/13" : { 2059 | "score" : 4.09 2060 | } 2061 | }, 2062 | { 2063 | "2010::/12" : { 2064 | "score" : 4.1 2065 | } 2066 | }, 2067 | { 2068 | "2020::/11" : { 2069 | "score" : 4.11 2070 | } 2071 | }, 2072 | { 2073 | "2040::/10" : { 2074 | "score" : 4.12 2075 | } 2076 | }, 2077 | { 2078 | "2080::/9" : { 2079 | "score" : 4.13 2080 | } 2081 | }, 2082 | { 2083 | "2100::/8" : { 2084 | "score" : 4.14 2085 | } 2086 | }, 2087 | { 2088 | "2200::/7" : { 2089 | "score" : 4.15 2090 | } 2091 | }, 2092 | { 2093 | "2400::/6" : { 2094 | "score" : 4.16 2095 | } 2096 | }, 2097 | { 2098 | "2800::/5" : { 2099 | "score" : 4.17 2100 | } 2101 | }, 2102 | { 2103 | "3000::/4" : { 2104 | "score" : 4.18 2105 | } 2106 | }, 2107 | { 2108 | "4000::/2" : { 2109 | "score" : 4.19 2110 | } 2111 | }, 2112 | { 2113 | "8000::/2" : { 2114 | "score" : 4.2 2115 | } 2116 | }, 2117 | { 2118 | "c000::/3" : { 2119 | "score" : 4.21 2120 | } 2121 | }, 2122 | { 2123 | "e000::/4" : { 2124 | "score" : 4.22 2125 | } 2126 | }, 2127 | { 2128 | "f000::/5" : { 2129 | "score" : 4.23 2130 | } 2131 | }, 2132 | { 2133 | "f800::/6" : { 2134 | "score" : 4.24 2135 | } 2136 | }, 2137 | { 2138 | "fe00::/9" : { 2139 | "score" : 4.25 2140 | } 2141 | }, 2142 | { 2143 | "fec0::/10" : { 2144 | "score" : 4.26 2145 | } 2146 | } 2147 | ] 2148 | -------------------------------------------------------------------------------- /test-data/GeoIP-Anonymous-Plus-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP-Anonymous-Plus-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Anonymous-IP-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Anonymous-IP-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-City-Shield-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-City-Shield-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-City-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-City-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Connection-Type-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Connection-Type-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Country-Shield-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Country-Shield-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Country-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Country-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-DensityIncome-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-DensityIncome-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Domain-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Domain-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Enterprise-Shield-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Enterprise-Shield-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Enterprise-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Enterprise-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-IP-Risk-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-IP-Risk-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-ISP-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-ISP-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Precision-Enterprise-Shield-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Precision-Enterprise-Shield-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Precision-Enterprise-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Precision-Enterprise-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-Static-IP-Score-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-Static-IP-Score-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoIP2-User-Count-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoIP2-User-Count-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoLite2-ASN-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoLite2-ASN-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoLite2-City-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoLite2-City-Test.mmdb -------------------------------------------------------------------------------- /test-data/GeoLite2-Country-Test.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/GeoLite2-Country-Test.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-no-ipv4-search-tree.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-string-value-entries.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-string-value-entries.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-broken-pointers-24.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-broken-pointers-24.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-broken-search-tree-24.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-decoder.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-decoder.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-ipv4-24.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-ipv4-24.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-ipv4-28.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-ipv4-28.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-ipv4-32.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-ipv4-32.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-ipv6-24.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-ipv6-24.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-ipv6-28.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-ipv6-28.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-ipv6-32.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-ipv6-32.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-metadata-pointers.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-metadata-pointers.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-mixed-24.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-mixed-24.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-mixed-28.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-mixed-28.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-mixed-32.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-mixed-32.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-nested.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-nested.mmdb -------------------------------------------------------------------------------- /test-data/MaxMind-DB-test-pointer-decoder.mmdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/MaxMind-DB-test-pointer-decoder.mmdb -------------------------------------------------------------------------------- /test-data/README.md: -------------------------------------------------------------------------------- 1 | ## How to generate test data 2 | Use the [write-test-data](https://github.com/maxmind/MaxMind-DB/blob/main/cmd/write-test-data) 3 | go tool to create a small set of test databases with a variety of data and 4 | record sizes. 5 | 6 | These test databases are useful for testing code that reads MaxMind DB files. 7 | 8 | There are several ways to figure out what IP addresses are actually in the 9 | test databases. You can take a look at the 10 | [source-data directory](https://github.com/maxmind/MaxMind-DB/tree/main/source-data) 11 | in this repository. This directory contains JSON files which are used to 12 | generate many (but not all) of the database files. 13 | 14 | You can also use the 15 | [mmdb-dump-database script](https://github.com/maxmind/MaxMind-DB-Reader-perl/blob/main/eg/mmdb-dump-database) 16 | in the 17 | [MaxMind-DB-Reader-perl repository](https://github.com/maxmind/MaxMind-DB-Reader-perl). 18 | 19 | ## Static test data 20 | Some of the test files are remnants of the 21 | [old perl test data writer](https://github.com/maxmind/MaxMind-DB/blob/f0a85c671c5b6e9c5e514bd66162724ee1dedea3/test-data/write-test-data.pl) 22 | and cannot be generated with the go tool. These databases are intentionally broken, 23 | and exploited functionality simply not available in the go mmdbwriter: 24 | 25 | - MaxMind-DB-test-broken-pointers-24.mmdb 26 | - MaxMind-DB-test-broken-search-tree-24.mmdb 27 | - MaxMind-DB-test-pointer-decoder.mmdb 28 | - GeoIP2-City-Test-Broken-Double-Format.mmdb 29 | - GeoIP2-City-Test-Invalid-Node-Count.mmdb 30 | - maps-with-pointers.raw 31 | 32 | ## Usage 33 | ``` 34 | Usage of ./write-test-data: 35 | -source string 36 | Source data directory 37 | -target string 38 | Destination directory for the generated mmdb files 39 | ``` 40 | 41 | Example: 42 | `./write-test-data --source ../../source-data --target ../../test-data` 43 | -------------------------------------------------------------------------------- /test-data/maps-with-pointers.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmind/MaxMind-DB/1485e1ddda800e53928ebacc6a77fb102d893c60/test-data/maps-with-pointers.raw -------------------------------------------------------------------------------- /tidyall.ini: -------------------------------------------------------------------------------- 1 | [PerlTidy] 2 | select = **/*.{pl,pm,t} 3 | 4 | [JSON] 5 | select = **/*.json 6 | --------------------------------------------------------------------------------