Add an optional IP-to-country lookup behind a cmake option - #147
Open
jagerman wants to merge 1 commit into
Open
Conversation
Every client draws a path screen naming the country of each hop, and each of them currently ships and maintains its own geo database to do it. This puts the lookup upstream: session::ip_country::lookup(ipv4) returns an ISO 3166-1 alpha-2 code, with available(), attribution() and database_version() alongside it. It is off by default. With WITH_IP_GEOLOCATION off the compiled-in database is an empty one rather than absent, so every lookup misses and a client needs no #ifdef of its own; available() is a link-time fact, not a macro, so nothing about the option reaches a header. data.cpp and no_data.cpp define the same accessors and the lookup itself is identical either way -- only the table it searches differs. DB-IP Lite is the source, chosen over MaxMind's GeoLite2 on licensing rather than accuracy: CC BY 4.0 permits redistribution and has no clause requiring a copy to stay current, which is what makes a bundled snapshot viable at all. Its IPv4 rows already tile the address space with no gaps and no mergeable neighbours, so a range needs only its first address -- the next range's start ends it. That leaves parallel arrays of 357k ipv4 starts and uint8_t country indices plus a 246-entry code table: 5 bytes a range, 1.79MB, of which a binary search touches only the 1.43MB of starts. The codes are numbered by descending range count. The countries holding the most ranges get the shortest indices, which is worth ~0.4MB of generated source, and the rare countries -- the ones that come and go between releases -- land at the end where nothing follows them to renumber. Numbering them alphabetically instead rewrote 86% of the table across the Aug->Sep refresh, where two mid-alphabet countries disappeared; this ordering rewrote 9%. The generated table is not committed: utils/update-ip-country-db.py downloads a release and generates it, and cmake refuses to configure with the option on until it has been run. Nothing downloads during a build. The tests run in both configurations and check the mechanism -- table invariants, range boundaries, the unknown path -- rather than pinning countries, save for one anchor commented as expected to move when the snapshot does.
mpretty-cyro
approved these changes
Sep 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every client draws a path screen naming the country of each hop, and each of them currently ships and maintains its own geo database to do it. This puts the lookup upstream: session::ip_country::lookup(ipv4) returns an ISO 3166-1 alpha-2 code, with available(), attribution() and database_version() alongside it.
It is off by default. With WITH_IP_GEOLOCATION off the compiled-in database is an empty one rather than absent, so every lookup misses and a client needs no #ifdef of its own; available() is a link-time fact, not a macro, so nothing about the option reaches a header. data.cpp and no_data.cpp define the same accessors and the lookup itself is identical either way -- only the table it searches differs.
DB-IP Lite is the source, chosen over MaxMind's GeoLite2 on licensing rather than accuracy: CC BY 4.0 permits redistribution and has no clause requiring a copy to stay current, which is what makes a bundled snapshot viable at all. Its IPv4 rows already tile the address space with no gaps and no mergeable neighbours, so a range needs only its first address -- the next range's start ends it. That leaves parallel arrays of 357k ipv4 starts and uint8_t country indices plus a 246-entry code table: 5 bytes a range, 1.79MB, of which a binary search touches only the 1.43MB of starts.
The codes are numbered by descending range count. The countries holding the most ranges get the shortest indices, which is worth ~0.4MB of generated source, and the rare countries -- the ones that come and go between releases -- land at the end where nothing follows them to renumber. Numbering them alphabetically instead rewrote 86% of the table across the Aug->Sep refresh, where two mid-alphabet countries disappeared; this ordering rewrote 9%.
The generated table is not committed: utils/update-ip-country-db.py downloads a release and generates it, and cmake refuses to configure with the option on until it has been run. Nothing downloads during a build. The tests run in both configurations and check the mechanism -- table invariants, range boundaries, the unknown path -- rather than pinning countries, save for one anchor commented as expected to move when the snapshot does.