maxminddb-0.24.0/.cargo_vcs_info.json0000644000000001360000000000100130620ustar { "git": { "sha1": "9045bfe1ac1fa3e7a29258692a03ea6ab2e01069" }, "path_in_vcs": "" }maxminddb-0.24.0/Cargo.lock0000644000000525570000000000100110530ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" [[package]] name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "bitflags" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bumpalo" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" [[package]] name = "ciborium-ll" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", "lazy_static", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" dependencies = [ "autocfg", "cfg-if 1.0.0", "lazy_static", ] [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" [[package]] name = "env_logger" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "errno" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", "windows-sys", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "fake" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "704749d52f737f28d7a632d4608db4005822efbb5a1c673c159f1193c9e63832" dependencies = [ "rand", ] [[package]] name = "getrandom" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", "libc", "wasi", ] [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hermit-abi" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "humantime" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" [[package]] name = "ipnetwork" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" dependencies = [ "serde", ] [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.1", "rustix", "windows-sys", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "js-sys" version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ "cfg-if 0.1.10", ] [[package]] name = "maxminddb" version = "0.24.0" dependencies = [ "criterion", "env_logger", "fake", "ipnetwork", "log", "memchr", "memmap2", "rayon", "serde", ] [[package]] name = "memchr" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "memmap2" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" dependencies = [ "libc", ] [[package]] name = "memoffset" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ "autocfg", ] [[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ "hermit-abi 0.1.13", "libc", ] [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" version = "11.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358" [[package]] name = "plotters" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" [[package]] name = "plotters-svg" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro2" version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", "rand_chacha", "rand_core", "rand_hc", ] [[package]] name = "rand_chacha" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ "rand_core", ] [[package]] name = "rayon" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg", "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", "lazy_static", "num_cpus", ] [[package]] name = "regex" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rustix" version = "0.38.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "tinytemplate" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "walkdir" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ "same-file", "winapi", "winapi-util", ] [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" maxminddb-0.24.0/Cargo.toml0000644000000033170000000000100110640ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "maxminddb" version = "0.24.0" authors = ["Gregory J. Oschwald "] include = [ "/Cargo.toml", "/benches/*.rs", "/src/**/*.rs", "/README.md", "/LICENSE", ] description = "Library for reading MaxMind DB format used by GeoIP2 and GeoLite2" homepage = "https://github.com/oschwald/maxminddb-rust" documentation = "http://oschwald.github.io/maxminddb-rust/maxminddb/struct.Reader.html" readme = "README.md" keywords = [ "MaxMind", "GeoIP2", "GeoIP", "geolocation", "ip", ] categories = [ "database", "network-programming", ] license = "ISC" repository = "https://github.com/oschwald/maxminddb-rust" [lib] name = "maxminddb" path = "src/maxminddb/lib.rs" [[bench]] name = "lookup" harness = false [dependencies.ipnetwork] version = "0.20.0" [dependencies.log] version = "0.4" [dependencies.memchr] version = "2.4" [dependencies.memmap2] version = "0.9.0" optional = true [dependencies.serde] version = "1.0" features = ["derive"] [dev-dependencies.criterion] version = "0.5" [dev-dependencies.env_logger] version = "0.10" [dev-dependencies.fake] version = "2.4" [dev-dependencies.rayon] version = "1.5" [features] default = [] mmap = ["memmap2"] unsafe-str-decode = [] maxminddb-0.24.0/Cargo.toml.orig000064400000000000000000000020361046102023000145420ustar 00000000000000[package] name = "maxminddb" version = "0.24.0" authors = [ "Gregory J. Oschwald " ] description = "Library for reading MaxMind DB format used by GeoIP2 and GeoLite2" readme = "README.md" keywords = ["MaxMind", "GeoIP2", "GeoIP", "geolocation", "ip"] categories = ["database", "network-programming"] homepage = "https://github.com/oschwald/maxminddb-rust" documentation = "http://oschwald.github.io/maxminddb-rust/maxminddb/struct.Reader.html" repository = "https://github.com/oschwald/maxminddb-rust" license = "ISC" include = ["/Cargo.toml", "/benches/*.rs", "/src/**/*.rs", "/README.md", "/LICENSE"] edition = "2021" [features] default = [] mmap = ["memmap2"] unsafe-str-decode = [] [lib] name ="maxminddb" path = "src/maxminddb/lib.rs" [dependencies] ipnetwork = "0.20.0" log = "0.4" serde = { version = "1.0", features = ["derive"] } memchr = "2.4" memmap2 = { version = "0.9.0", optional = true } [dev-dependencies] env_logger = "0.10" criterion = "0.5" fake = "2.4" rayon = "1.5" [[bench]] name = "lookup" harness = false maxminddb-0.24.0/LICENSE000064400000000000000000000014041046102023000126560ustar 00000000000000ISC License Copyright (c) 2015, Gregory J. Oschwald Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. maxminddb-0.24.0/README.md000064400000000000000000000035301046102023000131320ustar 00000000000000# Rust MaxMind DB Reader # [![crates.io]( https://img.shields.io/crates/v/maxminddb.svg)](https://crates.io/crates/maxminddb) [![Released API docs](https://docs.rs/maxminddb/badge.svg)](http://docs.rs/maxminddb) [![Master API docs](https://img.shields.io/badge/docs-master-green.svg)](https://oschwald.github.io/maxminddb-rust/) This library reads the MaxMind DB format, including the GeoIP2 and GeoLite2 databases. ## Building ## To build everything: ``` cargo build ``` ## Testing ## This crate manages its test data within a git submodule. To run the tests, you will first need to run the following command. ```bash git submodule update --init ``` ## Usage ## Add this to your `Cargo.toml`: ```toml [dependencies] maxminddb = "0.17" ``` and this to your crate root: ```rust extern crate maxminddb; ``` ## API Documentation ## The API docs are on [GitHub Pages](http://oschwald.github.io/maxminddb-rust/maxminddb/struct.Reader.html). ## Example ## See [`examples/lookup.rs`](https://github.com/oschwald/maxminddb-rust/blob/master/examples/lookup.rs) for a basic example. ## Benchmarks ## The projects include benchmarks using [Criterion.rs](https://github.com/bheisler/criterion.rs). First you need to have a working copy of the GeoIP City database. You can fetch it from [here](https://dev.maxmind.com/geoip/geoip2/geolite2/). Place it in the root folder as `GeoIP2-City.mmdb`. Once this is done, run ``` cargo bench ``` If [gnuplot](http://www.gnuplot.info/) is installed, Criterion.rs can generate an HTML report displaying the results of the benchmark under `target/criterion/report/index.html`. Result of doing 100 random IP lookups: ![](/assets/pdf_small.svg) ## Contributing ## Contributions welcome! Please fork the repository and open a pull request with your changes. ## License ## This is free software, licensed under the ISC license. maxminddb-0.24.0/benches/lookup.rs000064400000000000000000000032541046102023000151440ustar 00000000000000#[macro_use] extern crate criterion; extern crate fake; extern crate maxminddb; extern crate rayon; use criterion::Criterion; use fake::faker::internet::raw::IPv4; use fake::locales::EN; use fake::Fake; use maxminddb::geoip2; use rayon::prelude::*; use std::net::IpAddr; use std::str::FromStr; // Generate `count` IPv4 addresses #[must_use] pub fn generate_ipv4(count: u64) -> Vec { let mut ips = Vec::new(); for _i in 0..count { let val: String = IPv4(EN).fake(); let ip: IpAddr = FromStr::from_str(&val).unwrap(); ips.push(ip); } ips } // Single-threaded pub fn bench_maxminddb(ips: &[IpAddr], reader: &maxminddb::Reader>) { for ip in ips.iter() { let _ = reader.lookup::(*ip); } } // Using rayon for parallel execution pub fn bench_par_maxminddb(ips: &[IpAddr], reader: &maxminddb::Reader>) { ips.par_iter().for_each(|ip| { let _ = reader.lookup::(*ip); }); } pub fn criterion_benchmark(c: &mut Criterion) { let ips = generate_ipv4(100); let reader = maxminddb::Reader::open_readfile("GeoLite2-City.mmdb").unwrap(); c.bench_function("maxminddb", |b| b.iter(|| bench_maxminddb(&ips, &reader))); } pub fn criterion_par_benchmark(c: &mut Criterion) { let ips = generate_ipv4(100); let reader = maxminddb::Reader::open_readfile("GeoLite2-City.mmdb").unwrap(); c.bench_function("maxminddb_par", |b| { b.iter(|| bench_par_maxminddb(&ips, &reader)) }); } criterion_group! { name = benches; config = Criterion::default() .sample_size(10); targets = criterion_benchmark, criterion_par_benchmark } criterion_main!(benches); maxminddb-0.24.0/src/maxminddb/decoder.rs000064400000000000000000000326511046102023000163660ustar 00000000000000use log::debug; use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor}; use serde::forward_to_deserialize_any; use serde::serde_if_integer128; use std::convert::TryInto; use super::MaxMindDBError; use super::MaxMindDBError::DecodingError; fn to_usize(base: u8, bytes: &[u8]) -> usize { bytes .iter() .fold(base as usize, |acc, &b| (acc << 8) | b as usize) } enum Value<'a, 'de> { Any { prev_ptr: usize }, Bytes(&'de [u8]), String(&'de str), Bool(bool), I32(i32), U16(u16), U32(u32), U64(u64), U128(u128), F64(f64), F32(f32), Map(MapAccessor<'a, 'de>), Array(ArrayAccess<'a, 'de>), } #[derive(Debug)] pub struct Decoder<'de> { buf: &'de [u8], current_ptr: usize, } impl<'de> Decoder<'de> { pub fn new(buf: &'de [u8], start_ptr: usize) -> Decoder<'de> { Decoder { buf, current_ptr: start_ptr, } } fn eat_byte(&mut self) -> u8 { let b = self.buf[self.current_ptr]; self.current_ptr += 1; b } fn size_from_ctrl_byte(&mut self, ctrl_byte: u8, type_num: u8) -> usize { let size = (ctrl_byte & 0x1f) as usize; // extended if type_num == 0 { return size; } let bytes_to_read = if size > 28 { size - 28 } else { 0 }; let new_offset = self.current_ptr + bytes_to_read; let size_bytes = &self.buf[self.current_ptr..new_offset]; self.current_ptr = new_offset; match size { s if s < 29 => s, 29 => 29_usize + size_bytes[0] as usize, 30 => 285_usize + to_usize(0, size_bytes), _ => 65_821_usize + to_usize(0, size_bytes), } } fn size_and_type(&mut self) -> (usize, u8) { let ctrl_byte = self.eat_byte(); let mut type_num = ctrl_byte >> 5; // Extended type if type_num == 0 { type_num = self.eat_byte() + 7; } let size = self.size_from_ctrl_byte(ctrl_byte, type_num); (size, type_num) } fn decode_any>(&mut self, visitor: V) -> DecodeResult { match self.decode_any_value()? { Value::Any { prev_ptr } => { let res = self.decode_any(visitor); self.current_ptr = prev_ptr; res } Value::Bool(x) => visitor.visit_bool(x), Value::Bytes(x) => visitor.visit_borrowed_bytes(x), Value::String(x) => visitor.visit_borrowed_str(x), Value::I32(x) => visitor.visit_i32(x), Value::U16(x) => visitor.visit_u16(x), Value::U32(x) => visitor.visit_u32(x), Value::U64(x) => visitor.visit_u64(x), Value::U128(x) => visitor.visit_u128(x), Value::F64(x) => visitor.visit_f64(x), Value::F32(x) => visitor.visit_f32(x), Value::Map(x) => visitor.visit_map(x), Value::Array(x) => visitor.visit_seq(x), } } fn decode_any_value(&mut self) -> DecodeResult> { let (size, type_num) = self.size_and_type(); Ok(match type_num { 1 => { let new_ptr = self.decode_pointer(size); let prev_ptr = self.current_ptr; self.current_ptr = new_ptr; Value::Any { prev_ptr } } 2 => Value::String(self.decode_string(size)?), 3 => Value::F64(self.decode_double(size)?), 4 => Value::Bytes(self.decode_bytes(size)?), 5 => Value::U16(self.decode_uint16(size)?), 6 => Value::U32(self.decode_uint32(size)?), 7 => self.decode_map(size), 8 => Value::I32(self.decode_int(size)?), 9 => Value::U64(self.decode_uint64(size)?), 10 => { serde_if_integer128! { return Ok(Value::U128(self.decode_uint128(size)?)); } #[allow(unreachable_code)] Value::Bytes(self.decode_bytes(size)?) } 11 => self.decode_array(size), 14 => Value::Bool(self.decode_bool(size)?), 15 => Value::F32(self.decode_float(size)?), u => { return Err(MaxMindDBError::InvalidDatabaseError(format!( "Unknown data type: {u:?}" ))) } }) } fn decode_array(&mut self, size: usize) -> Value<'_, 'de> { Value::Array(ArrayAccess { de: self, count: size, }) } fn decode_bool(&mut self, size: usize) -> DecodeResult { match size { 0 | 1 => Ok(size != 0), s => Err(MaxMindDBError::InvalidDatabaseError(format!( "bool of size {s:?}" ))), } } fn decode_bytes(&mut self, size: usize) -> DecodeResult<&'de [u8]> { let new_offset = self.current_ptr + size; let u8_slice = &self.buf[self.current_ptr..new_offset]; self.current_ptr = new_offset; Ok(u8_slice) } fn decode_float(&mut self, size: usize) -> DecodeResult { let new_offset = self.current_ptr + size; let value: [u8; 4] = self.buf[self.current_ptr..new_offset] .try_into() .map_err(|_| { MaxMindDBError::InvalidDatabaseError(format!( "float of size {:?}", new_offset - self.current_ptr )) })?; self.current_ptr = new_offset; let float_value = f32::from_be_bytes(value); Ok(float_value) } fn decode_double(&mut self, size: usize) -> DecodeResult { let new_offset = self.current_ptr + size; let value: [u8; 8] = self.buf[self.current_ptr..new_offset] .try_into() .map_err(|_| { MaxMindDBError::InvalidDatabaseError(format!( "double of size {:?}", new_offset - self.current_ptr )) })?; self.current_ptr = new_offset; let float_value = f64::from_be_bytes(value); Ok(float_value) } fn decode_uint64(&mut self, size: usize) -> DecodeResult { match size { s if s <= 8 => { let new_offset = self.current_ptr + size; let value = self.buf[self.current_ptr..new_offset] .iter() .fold(0_u64, |acc, &b| (acc << 8) | u64::from(b)); self.current_ptr = new_offset; Ok(value) } s => Err(MaxMindDBError::InvalidDatabaseError(format!( "u64 of size {s:?}" ))), } } serde_if_integer128! { fn decode_uint128( &mut self, size: usize, ) -> DecodeResult { match size { s if s <= 16 => { let new_offset = self.current_ptr + size; let value = self.buf[self.current_ptr..new_offset] .iter() .fold(0_u128, |acc, &b| (acc << 8) | u128::from(b)); self.current_ptr = new_offset; Ok(value) } s => Err(MaxMindDBError::InvalidDatabaseError(format!( "u128 of size {s:?}" ))), } } } fn decode_uint32(&mut self, size: usize) -> DecodeResult { match size { s if s <= 4 => { let new_offset = self.current_ptr + size; let value = self.buf[self.current_ptr..new_offset] .iter() .fold(0_u32, |acc, &b| (acc << 8) | u32::from(b)); self.current_ptr = new_offset; Ok(value) } s => Err(MaxMindDBError::InvalidDatabaseError(format!( "u32 of size {s:?}" ))), } } fn decode_uint16(&mut self, size: usize) -> DecodeResult { match size { s if s <= 2 => { let new_offset = self.current_ptr + size; let value = self.buf[self.current_ptr..new_offset] .iter() .fold(0_u16, |acc, &b| (acc << 8) | u16::from(b)); self.current_ptr = new_offset; Ok(value) } s => Err(MaxMindDBError::InvalidDatabaseError(format!( "u16 of size {s:?}" ))), } } fn decode_int(&mut self, size: usize) -> DecodeResult { match size { s if s <= 4 => { let new_offset = self.current_ptr + size; let value = self.buf[self.current_ptr..new_offset] .iter() .fold(0_i32, |acc, &b| (acc << 8) | i32::from(b)); self.current_ptr = new_offset; Ok(value) } s => Err(MaxMindDBError::InvalidDatabaseError(format!( "int32 of size {s:?}" ))), } } fn decode_map(&mut self, size: usize) -> Value<'_, 'de> { Value::Map(MapAccessor { de: self, count: size * 2, }) } fn decode_pointer(&mut self, size: usize) -> usize { let pointer_value_offset = [0, 0, 2048, 526_336, 0]; let pointer_size = ((size >> 3) & 0x3) + 1; let new_offset = self.current_ptr + pointer_size; let pointer_bytes = &self.buf[self.current_ptr..new_offset]; self.current_ptr = new_offset; let base = if pointer_size == 4 { 0 } else { (size & 0x7) as u8 }; let unpacked = to_usize(base, pointer_bytes); unpacked + pointer_value_offset[pointer_size] } #[cfg(feature = "unsafe-str-decode")] fn decode_string(&mut self, size: usize) -> DecodeResult<&'de str> { use std::str::from_utf8_unchecked; let new_offset: usize = self.current_ptr + size; let bytes = &self.buf[self.current_ptr..new_offset]; self.current_ptr = new_offset; // SAFETY: // A corrupt maxminddb will cause undefined behaviour. // If the caller has verified the integrity of their database and trusts their upstream // provider, they can opt-into the performance gains provided by this unsafe function via // the `unsafe-str-decode` feature flag. // This can provide around 20% performance increase in the lookup benchmark. let v = unsafe { from_utf8_unchecked(bytes) }; Ok(v) } #[cfg(not(feature = "unsafe-str-decode"))] fn decode_string(&mut self, size: usize) -> DecodeResult<&'de str> { use std::str::from_utf8; let new_offset: usize = self.current_ptr + size; let bytes = &self.buf[self.current_ptr..new_offset]; self.current_ptr = new_offset; match from_utf8(bytes) { Ok(v) => Ok(v), Err(_) => Err(MaxMindDBError::InvalidDatabaseError( "error decoding string".to_owned(), )), } } } pub type DecodeResult = Result; impl<'de: 'a, 'a> de::Deserializer<'de> for &'a mut Decoder<'de> { type Error = MaxMindDBError; fn deserialize_any(self, visitor: V) -> DecodeResult where V: Visitor<'de>, { debug!("deserialize_any"); self.decode_any(visitor) } fn deserialize_option(self, visitor: V) -> DecodeResult where V: Visitor<'de>, { debug!("deserialize_option"); visitor.visit_some(self) } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } struct ArrayAccess<'a, 'de: 'a> { de: &'a mut Decoder<'de>, count: usize, } // `SeqAccess` is provided to the `Visitor` to give it the ability to iterate // through elements of the sequence. impl<'de, 'a> SeqAccess<'de> for ArrayAccess<'a, 'de> { type Error = MaxMindDBError; fn next_element_seed(&mut self, seed: T) -> DecodeResult> where T: DeserializeSeed<'de>, { // Check if there are no more elements. if self.count == 0 { return Ok(None); } self.count -= 1; // Deserialize an array element. seed.deserialize(&mut *self.de).map(Some) } } struct MapAccessor<'a, 'de: 'a> { de: &'a mut Decoder<'de>, count: usize, } // `MapAccess` is provided to the `Visitor` to give it the ability to iterate // through entries of the map. impl<'de, 'a> MapAccess<'de> for MapAccessor<'a, 'de> { type Error = MaxMindDBError; fn next_key_seed(&mut self, seed: K) -> DecodeResult> where K: DeserializeSeed<'de>, { // Check if there are no more entries. if self.count == 0 { return Ok(None); } self.count -= 1; // Deserialize a map key. seed.deserialize(&mut *self.de).map(Some) } fn next_value_seed(&mut self, seed: V) -> DecodeResult where V: DeserializeSeed<'de>, { // Check if there are no more entries. if self.count == 0 { return Err(DecodingError("no more entries".to_owned())); } self.count -= 1; // Deserialize a map value. seed.deserialize(&mut *self.de) } } maxminddb-0.24.0/src/maxminddb/geoip2.rs000064400000000000000000000165451046102023000161520ustar 00000000000000use serde::{Deserialize, Serialize}; /// GeoIP2 Country record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Country<'a> { #[serde(borrow)] pub continent: Option>, pub country: Option>, pub registered_country: Option>, pub represented_country: Option>, pub traits: Option, } /// GeoIP2 City record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct City<'a> { #[serde(borrow)] pub city: Option>, pub continent: Option>, pub country: Option>, pub location: Option>, pub postal: Option>, pub registered_country: Option>, pub represented_country: Option>, pub subdivisions: Option>>, pub traits: Option, } /// GeoIP2 Enterprise record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Enterprise<'a> { #[serde(borrow)] pub city: Option>, pub continent: Option>, pub country: Option>, pub location: Option>, pub postal: Option>, pub registered_country: Option>, pub represented_country: Option>, pub subdivisions: Option>>, pub traits: Option>, } /// GeoIP2 ISP record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Isp<'a> { pub autonomous_system_number: Option, pub autonomous_system_organization: Option<&'a str>, pub isp: Option<&'a str>, pub mobile_country_code: Option<&'a str>, pub mobile_network_code: Option<&'a str>, pub organization: Option<&'a str>, } /// GeoIP2 Connection-Type record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct ConnectionType<'a> { pub connection_type: Option<&'a str>, } /// GeoIP2 Anonymous Ip record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct AnonymousIp { pub is_anonymous: Option, pub is_anonymous_vpn: Option, pub is_hosting_provider: Option, pub is_public_proxy: Option, pub is_residential_proxy: Option, pub is_tor_exit_node: Option, } /// GeoIP2 DensityIncome record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct DensityIncome { pub average_income: Option, pub population_density: Option, } /// GeoIP2 Domain record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Domain<'a> { pub domain: Option<&'a str>, } /// GeoIP2 Asn record #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Asn<'a> { pub autonomous_system_number: Option, pub autonomous_system_organization: Option<&'a str>, } /// Country model structs pub mod country { use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Continent<'a> { pub code: Option<&'a str>, pub geoname_id: Option, pub names: Option>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Country<'a> { pub geoname_id: Option, pub is_in_european_union: Option, pub iso_code: Option<&'a str>, pub names: Option>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct RepresentedCountry<'a> { pub geoname_id: Option, pub is_in_european_union: Option, pub iso_code: Option<&'a str>, pub names: Option>, #[serde(rename = "type")] pub representation_type: Option<&'a str>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Traits { pub is_anonymous_proxy: Option, pub is_anycast: Option, pub is_satellite_provider: Option, } } /// City model structs pub mod city { use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; pub use super::country::{Continent, Country, RepresentedCountry, Traits}; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct City<'a> { pub geoname_id: Option, #[serde(borrow)] pub names: Option>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Location<'a> { pub accuracy_radius: Option, pub latitude: Option, pub longitude: Option, pub metro_code: Option, pub time_zone: Option<&'a str>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Postal<'a> { pub code: Option<&'a str>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Subdivision<'a> { pub geoname_id: Option, pub iso_code: Option<&'a str>, pub names: Option>, } } /// Enterprise model structs pub mod enterprise { use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; pub use super::country::{Continent, RepresentedCountry}; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct City<'a> { pub confidence: Option, pub geoname_id: Option, #[serde(borrow)] pub names: Option>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Country<'a> { pub confidence: Option, pub geoname_id: Option, pub is_in_european_union: Option, pub iso_code: Option<&'a str>, pub names: Option>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Location<'a> { pub accuracy_radius: Option, pub latitude: Option, pub longitude: Option, pub metro_code: Option, pub time_zone: Option<&'a str>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Postal<'a> { pub code: Option<&'a str>, pub confidence: Option, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Subdivision<'a> { pub confidence: Option, pub geoname_id: Option, pub iso_code: Option<&'a str>, pub names: Option>, } #[derive(Deserialize, Serialize, Clone, Debug)] pub struct Traits<'a> { pub autonomous_system_number: Option, pub autonomous_system_organization: Option<&'a str>, pub connection_type: Option<&'a str>, pub domain: Option<&'a str>, pub is_anonymous: Option, pub is_anonymous_proxy: Option, pub is_anonymous_vpn: Option, pub is_anycast: Option, pub is_hosting_provider: Option, pub isp: Option<&'a str>, pub is_public_proxy: Option, pub is_residential_proxy: Option, pub is_satellite_provider: Option, pub is_tor_exit_node: Option, pub mobile_country_code: Option<&'a str>, pub mobile_network_code: Option<&'a str>, pub organization: Option<&'a str>, pub user_type: Option<&'a str>, } } maxminddb-0.24.0/src/maxminddb/lib.rs000064400000000000000000000460711046102023000155300ustar 00000000000000#![deny(trivial_casts, trivial_numeric_casts, unused_import_braces)] use std::cmp::Ordering; use std::collections::BTreeMap; use std::fmt::{self, Display, Formatter}; use std::io; use std::marker::PhantomData; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::path::Path; use ipnetwork::IpNetwork; use serde::{de, Deserialize}; #[cfg(feature = "mmap")] pub use memmap2::Mmap; #[cfg(feature = "mmap")] use memmap2::MmapOptions; #[cfg(feature = "mmap")] use std::fs::File; #[derive(Debug, PartialEq, Eq)] pub enum MaxMindDBError { AddressNotFoundError(String), InvalidDatabaseError(String), IoError(String), MapError(String), DecodingError(String), InvalidNetworkError(String), } impl From for MaxMindDBError { fn from(err: io::Error) -> MaxMindDBError { // clean up and clean up MaxMindDBError generally MaxMindDBError::IoError(err.to_string()) } } impl Display for MaxMindDBError { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> { match self { MaxMindDBError::AddressNotFoundError(msg) => { write!(fmt, "AddressNotFoundError: {msg}")? } MaxMindDBError::InvalidDatabaseError(msg) => { write!(fmt, "InvalidDatabaseError: {msg}")? } MaxMindDBError::IoError(msg) => write!(fmt, "IoError: {msg}")?, MaxMindDBError::MapError(msg) => write!(fmt, "MapError: {msg}")?, MaxMindDBError::DecodingError(msg) => write!(fmt, "DecodingError: {msg}")?, MaxMindDBError::InvalidNetworkError(msg) => write!(fmt, "InvalidNetworkError: {msg}")?, } Ok(()) } } // Use default implementation for `std::error::Error` impl std::error::Error for MaxMindDBError {} impl de::Error for MaxMindDBError { fn custom(msg: T) -> Self { MaxMindDBError::DecodingError(format!("{msg}")) } } #[derive(Deserialize, Debug)] pub struct Metadata { pub binary_format_major_version: u16, pub binary_format_minor_version: u16, pub build_epoch: u64, pub database_type: String, pub description: BTreeMap, pub ip_version: u16, pub languages: Vec, pub node_count: u32, pub record_size: u16, } #[derive(Debug)] struct WithinNode { node: usize, ip_bytes: Vec, prefix_len: usize, } #[derive(Debug)] pub struct Within<'de, T: Deserialize<'de>, S: AsRef<[u8]>> { reader: &'de Reader, node_count: usize, stack: Vec, phantom: PhantomData<&'de T>, } #[derive(Debug)] pub struct WithinItem { pub ip_net: IpNetwork, pub info: T, } impl<'de, T: Deserialize<'de>, S: AsRef<[u8]>> Iterator for Within<'de, T, S> { type Item = Result, MaxMindDBError>; fn next(&mut self) -> Option { while let Some(current) = self.stack.pop() { let bit_count = current.ip_bytes.len() * 8; // Skip networks that are aliases for the IPv4 network if self.reader.ipv4_start != 0 && current.node == self.reader.ipv4_start && bit_count == 128 && current.ip_bytes[..12].iter().any(|&b| b != 0) { continue; } match current.node.cmp(&self.node_count) { Ordering::Greater => { // This is a data node, emit it and we're done (until the following next call) let ip_net = match bytes_and_prefix_to_net( ¤t.ip_bytes, current.prefix_len as u8, ) { Ok(ip_net) => ip_net, Err(e) => return Some(Err(e)), }; // TODO: should this block become a helper method on reader? let rec = match self.reader.resolve_data_pointer(current.node) { Ok(rec) => rec, Err(e) => return Some(Err(e)), }; let mut decoder = decoder::Decoder::new( &self.reader.buf.as_ref()[self.reader.pointer_base..], rec, ); return match T::deserialize(&mut decoder) { Ok(info) => Some(Ok(WithinItem { ip_net, info })), Err(e) => Some(Err(e)), }; } Ordering::Equal => { // Dead end, nothing to do } Ordering::Less => { // In order traversal of our children // right/1-bit let mut right_ip_bytes = current.ip_bytes.clone(); right_ip_bytes[current.prefix_len >> 3] |= 1 << ((bit_count - current.prefix_len - 1) % 8); let node = match self.reader.read_node(current.node, 1) { Ok(node) => node, Err(e) => return Some(Err(e)), }; self.stack.push(WithinNode { node, ip_bytes: right_ip_bytes, prefix_len: current.prefix_len + 1, }); // left/0-bit let node = match self.reader.read_node(current.node, 0) { Ok(node) => node, Err(e) => return Some(Err(e)), }; self.stack.push(WithinNode { node, ip_bytes: current.ip_bytes.clone(), prefix_len: current.prefix_len + 1, }); } } } None } } /// A reader for the MaxMind DB format. The lifetime `'data` is tied to the lifetime of the underlying buffer holding the contents of the database file. #[derive(Debug)] pub struct Reader> { buf: S, pub metadata: Metadata, ipv4_start: usize, pointer_base: usize, } #[cfg(feature = "mmap")] impl<'de> Reader { /// Open a MaxMind DB database file by memory mapping it. /// /// # Example /// /// ``` /// let reader = maxminddb::Reader::open_mmap("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap(); /// ``` pub fn open_mmap>(database: P) -> Result, MaxMindDBError> { let file_read = File::open(database)?; let mmap = unsafe { MmapOptions::new().map(&file_read) }?; Reader::from_source(mmap) } } impl Reader> { /// Open a MaxMind DB database file by loading it into memory. /// /// # Example /// /// ``` /// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap(); /// ``` pub fn open_readfile>(database: P) -> Result>, MaxMindDBError> { use std::fs; let buf: Vec = fs::read(&database)?; Reader::from_source(buf) } } impl<'de, S: AsRef<[u8]>> Reader { /// Open a MaxMind DB database from anything that implements AsRef<[u8]> /// /// # Example /// /// ``` /// use std::fs; /// let buf = fs::read("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap(); /// let reader = maxminddb::Reader::from_source(buf).unwrap(); /// ``` pub fn from_source(buf: S) -> Result, MaxMindDBError> { let data_section_separator_size = 16; let metadata_start = find_metadata_start(buf.as_ref())?; let mut type_decoder = decoder::Decoder::new(&buf.as_ref()[metadata_start..], 0); let metadata = Metadata::deserialize(&mut type_decoder)?; let search_tree_size = (metadata.node_count as usize) * (metadata.record_size as usize) / 4; let mut reader = Reader { buf, pointer_base: search_tree_size + data_section_separator_size, metadata, ipv4_start: 0, }; reader.ipv4_start = reader.find_ipv4_start()?; Ok(reader) } /// Lookup the socket address in the opened MaxMind DB /// /// Example: /// /// ``` /// use maxminddb::geoip2; /// use std::net::IpAddr; /// use std::str::FromStr; /// /// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap(); /// /// let ip: IpAddr = FromStr::from_str("89.160.20.128").unwrap(); /// let city: geoip2::City = reader.lookup(ip).unwrap(); /// print!("{:?}", city); /// ``` pub fn lookup(&'de self, address: IpAddr) -> Result where T: Deserialize<'de>, { self.lookup_prefix(address).map(|(v, _)| v) } /// Lookup the socket address in the opened MaxMind DB /// /// Example: /// /// ``` /// use maxminddb::geoip2; /// use std::net::IpAddr; /// use std::str::FromStr; /// /// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap(); /// /// let ip: IpAddr = "89.160.20.128".parse().unwrap(); /// let (city, prefix_len) = reader.lookup_prefix::(ip).unwrap(); /// print!("{:?}, prefix length: {}", city, prefix_len); /// ``` pub fn lookup_prefix(&'de self, address: IpAddr) -> Result<(T, usize), MaxMindDBError> where T: Deserialize<'de>, { let ip_bytes = ip_to_bytes(address); let (pointer, prefix_len) = self.find_address_in_tree(&ip_bytes)?; if pointer == 0 { return Err(MaxMindDBError::AddressNotFoundError( "Address not found in database".to_owned(), )); } let rec = self.resolve_data_pointer(pointer)?; let mut decoder = decoder::Decoder::new(&self.buf.as_ref()[self.pointer_base..], rec); T::deserialize(&mut decoder).map(|v| (v, prefix_len)) } /// Iterate over blocks of IP networks in the opened MaxMind DB /// /// Example: /// /// ``` /// use ipnetwork::IpNetwork; /// use maxminddb::{geoip2, Within}; /// /// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap(); /// /// let ip_net = IpNetwork::V6("::/0".parse().unwrap()); /// let mut iter: Within = reader.within(ip_net).unwrap(); /// while let Some(next) = iter.next() { /// let item = next.unwrap(); /// println!("ip_net={}, city={:?}", item.ip_net, item.info); /// } /// ``` pub fn within(&'de self, cidr: IpNetwork) -> Result, MaxMindDBError> where T: Deserialize<'de>, { let ip_address = cidr.network(); let prefix_len = cidr.prefix() as usize; let ip_bytes = ip_to_bytes(ip_address); let bit_count = ip_bytes.len() * 8; let mut node = self.start_node(bit_count); let node_count = self.metadata.node_count as usize; let mut stack: Vec = Vec::with_capacity(bit_count - prefix_len); // Traverse down the tree to the level that matches the cidr mark let mut i = 0_usize; while i < prefix_len { let bit = 1 & (ip_bytes[i >> 3] >> (7 - (i % 8))) as usize; node = self.read_node(node, bit)?; if node >= node_count { // We've hit a dead end before we exhausted our prefix break; } i += 1; } if node < node_count { // Ok, now anything that's below node in the tree is "within", start with the node we // traversed to as our to be processed stack. stack.push(WithinNode { node, ip_bytes, prefix_len, }); } // else the stack will be empty and we'll be returning an iterator that visits nothing, // which makes sense. let within: Within = Within { reader: self, node_count, stack, phantom: PhantomData, }; Ok(within) } fn find_address_in_tree(&self, ip_address: &[u8]) -> Result<(usize, usize), MaxMindDBError> { let bit_count = ip_address.len() * 8; let mut node = self.start_node(bit_count); let node_count = self.metadata.node_count as usize; let mut prefix_len = bit_count; for i in 0..bit_count { if node >= node_count { prefix_len = i; break; } let bit = 1 & (ip_address[i >> 3] >> (7 - (i % 8))); node = self.read_node(node, bit as usize)?; } match node_count { n if n == node => Ok((0, prefix_len)), n if node > n => Ok((node, prefix_len)), _ => Err(MaxMindDBError::InvalidDatabaseError( "invalid node in search tree".to_owned(), )), } } fn start_node(&self, length: usize) -> usize { if length == 128 { 0 } else { self.ipv4_start } } fn find_ipv4_start(&self) -> Result { if self.metadata.ip_version != 6 { return Ok(0); } // We are looking up an IPv4 address in an IPv6 tree. Skip over the // first 96 nodes. let mut node: usize = 0_usize; for _ in 0_u8..96 { if node >= self.metadata.node_count as usize { break; } node = self.read_node(node, 0)?; } Ok(node) } fn read_node(&self, node_number: usize, index: usize) -> Result { let buf = self.buf.as_ref(); let base_offset = node_number * (self.metadata.record_size as usize) / 4; let val = match self.metadata.record_size { 24 => { let offset = base_offset + index * 3; to_usize(0, &buf[offset..offset + 3]) } 28 => { let mut middle = buf[base_offset + 3]; if index != 0 { middle &= 0x0F } else { middle = (0xF0 & middle) >> 4 } let offset = base_offset + index * 4; to_usize(middle, &buf[offset..offset + 3]) } 32 => { let offset = base_offset + index * 4; to_usize(0, &buf[offset..offset + 4]) } s => { return Err(MaxMindDBError::InvalidDatabaseError(format!( "unknown record size: \ {s:?}" ))) } }; Ok(val) } fn resolve_data_pointer(&self, pointer: usize) -> Result { let resolved = pointer - (self.metadata.node_count as usize) - 16; if resolved > self.buf.as_ref().len() { return Err(MaxMindDBError::InvalidDatabaseError( "the MaxMind DB file's search tree \ is corrupt" .to_owned(), )); } Ok(resolved) } } // I haven't moved all patterns of this form to a generic function as // the FromPrimitive trait is unstable fn to_usize(base: u8, bytes: &[u8]) -> usize { bytes .iter() .fold(base as usize, |acc, &b| (acc << 8) | b as usize) } fn ip_to_bytes(address: IpAddr) -> Vec { match address { IpAddr::V4(a) => a.octets().to_vec(), IpAddr::V6(a) => a.octets().to_vec(), } } #[allow(clippy::many_single_char_names)] fn bytes_and_prefix_to_net(bytes: &[u8], prefix: u8) -> Result { let (ip, pre) = match bytes.len() { 4 => ( IpAddr::V4(Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3])), prefix, ), 16 => { if bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0 && bytes[3] == 0 && bytes[4] == 0 && bytes[5] == 0 && bytes[6] == 0 && bytes[7] == 0 && bytes[8] == 0 && bytes[9] == 0 && bytes[10] == 0 && bytes[11] == 0 { // It's actually v4, but in v6 form, convert would be nice if ipnetwork had this // logic. ( IpAddr::V4(Ipv4Addr::new(bytes[12], bytes[13], bytes[14], bytes[15])), prefix - 96, ) } else { let a = u16::from(bytes[0]) << 8 | u16::from(bytes[1]); let b = u16::from(bytes[2]) << 8 | u16::from(bytes[3]); let c = u16::from(bytes[4]) << 8 | u16::from(bytes[5]); let d = u16::from(bytes[6]) << 8 | u16::from(bytes[7]); let e = u16::from(bytes[8]) << 8 | u16::from(bytes[9]); let f = u16::from(bytes[10]) << 8 | u16::from(bytes[11]); let g = u16::from(bytes[12]) << 8 | u16::from(bytes[13]); let h = u16::from(bytes[14]) << 8 | u16::from(bytes[15]); (IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)), prefix) } } // This should never happen _ => { return Err(MaxMindDBError::InvalidNetworkError( "invalid address".to_owned(), )) } }; IpNetwork::new(ip, pre).map_err(|e| MaxMindDBError::InvalidNetworkError(e.to_string())) } fn find_metadata_start(buf: &[u8]) -> Result { const METADATA_START_MARKER: &[u8] = b"\xab\xcd\xefMaxMind.com"; memchr::memmem::rfind(buf, METADATA_START_MARKER) .map(|x| x + METADATA_START_MARKER.len()) .ok_or_else(|| { MaxMindDBError::InvalidDatabaseError( "Could not find MaxMind DB metadata in file.".to_owned(), ) }) } mod decoder; pub mod geoip2; #[cfg(test)] mod reader_test; #[cfg(test)] mod tests { use super::MaxMindDBError; #[test] fn test_error_display() { assert_eq!( format!( "{}", MaxMindDBError::AddressNotFoundError("something went wrong".to_owned()) ), "AddressNotFoundError: something went wrong".to_owned(), ); assert_eq!( format!( "{}", MaxMindDBError::InvalidDatabaseError("something went wrong".to_owned()) ), "InvalidDatabaseError: something went wrong".to_owned(), ); assert_eq!( format!( "{}", MaxMindDBError::IoError("something went wrong".to_owned()) ), "IoError: something went wrong".to_owned(), ); assert_eq!( format!( "{}", MaxMindDBError::MapError("something went wrong".to_owned()) ), "MapError: something went wrong".to_owned(), ); assert_eq!( format!( "{}", MaxMindDBError::DecodingError("something went wrong".to_owned()) ), "DecodingError: something went wrong".to_owned(), ); } } maxminddb-0.24.0/src/maxminddb/reader_test.rs000064400000000000000000000333031046102023000172550ustar 00000000000000use std::net::IpAddr; use std::str::FromStr; use serde::Deserialize; use super::{MaxMindDBError, Reader}; #[allow(clippy::float_cmp)] #[test] fn test_decoder() { let _ = env_logger::try_init(); #[allow(non_snake_case)] #[derive(Deserialize, Debug, Eq, PartialEq)] struct MapXType { arrayX: Vec, utf8_stringX: String, } #[allow(non_snake_case)] #[derive(Deserialize, Debug, Eq, PartialEq)] struct MapType { mapX: MapXType, } #[derive(Deserialize, Debug)] struct TestType<'a> { array: Vec, boolean: bool, bytes: &'a [u8], double: f64, float: f32, int32: i32, map: MapType, uint16: u16, uint32: u32, uint64: u64, uint128: u128, utf8_string: String, } let r = Reader::open_readfile("test-data/test-data/MaxMind-DB-test-decoder.mmdb"); if let Err(err) = r { panic!("error opening mmdb: {err:?}"); } let r = r.unwrap(); let ip: IpAddr = FromStr::from_str("1.1.1.0").unwrap(); let result: TestType = r.lookup(ip).unwrap(); assert_eq!(result.array, vec![1_u32, 2_u32, 3_u32]); assert!(result.boolean); assert_eq!(result.bytes, vec![0_u8, 0_u8, 0_u8, 42_u8]); assert_eq!(result.double, 42.123_456); assert_eq!(result.float, 1.1); assert_eq!(result.int32, -268_435_456); assert_eq!( result.map, MapType { mapX: MapXType { arrayX: vec![7, 8, 9], utf8_stringX: "hello".to_string(), }, } ); assert_eq!(result.uint16, 100); assert_eq!(result.uint32, 268_435_456); assert_eq!(result.uint64, 1_152_921_504_606_846_976); assert_eq!( result.uint128, 1_329_227_995_784_915_872_903_807_060_280_344_576 ); assert_eq!( result.utf8_string, "unicode! \u{262f} - \u{266b}".to_string() ); } #[test] fn test_pointers_in_metadata() { let _ = env_logger::try_init(); let r = Reader::open_readfile("test-data/test-data/MaxMind-DB-test-metadata-pointers.mmdb"); if let Err(err) = r { panic!("error opening mmdb: {err:?}"); } } #[test] fn test_broken_database() { let _ = env_logger::try_init(); let r = Reader::open_readfile("test-data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb") .ok() .unwrap(); let ip: IpAddr = FromStr::from_str("2001:220::").unwrap(); #[derive(Deserialize, Debug)] struct TestType {} match r.lookup::(ip) { Err(e) => assert_eq!( e, MaxMindDBError::InvalidDatabaseError("double of size 2".to_string()) ), Ok(_) => panic!("Error expected"), } } #[test] fn test_missing_database() { let _ = env_logger::try_init(); let r = Reader::open_readfile("file-does-not-exist.mmdb"); match r { Ok(_) => panic!("Received Reader when opening non-existent file"), Err(e) => assert!( e == MaxMindDBError::IoError( "The system cannot find the file specified. (os error 2)".to_string() ) || e == MaxMindDBError::IoError("No such file or directory (os error 2)".to_string()) ), } } #[test] fn test_non_database() { let _ = env_logger::try_init(); let r = Reader::open_readfile("README.md"); match r { Ok(_) => panic!("Received Reader when opening a non-MMDB file"), Err(e) => assert_eq!( e, MaxMindDBError::InvalidDatabaseError( "Could not find MaxMind DB metadata \ in file." .to_string(), ) ), } } #[test] fn test_reader() { let _ = env_logger::try_init(); let sizes = [24_usize, 28, 32]; for record_size in &sizes { let versions = [4_usize, 6]; for ip_version in &versions { let filename = format!("test-data/test-data/MaxMind-DB-test-ipv{ip_version}-{record_size}.mmdb"); let reader = Reader::open_readfile(filename).ok().unwrap(); check_metadata(&reader, *ip_version, *record_size); check_ip(&reader, *ip_version); } } } /// Create Reader by explicitly reading the entire file into a buffer. #[test] fn test_reader_readfile() { let _ = env_logger::try_init(); let sizes = [24_usize, 28, 32]; for record_size in &sizes { let versions = [4_usize, 6]; for ip_version in &versions { let filename = format!("test-data/test-data/MaxMind-DB-test-ipv{ip_version}-{record_size}.mmdb"); let reader = Reader::open_readfile(filename).ok().unwrap(); check_metadata(&reader, *ip_version, *record_size); check_ip(&reader, *ip_version); } } } #[test] #[cfg(feature = "mmap")] fn test_reader_mmap() { let _ = env_logger::try_init(); let sizes = [24usize, 28, 32]; for record_size in sizes.iter() { let versions = [4usize, 6]; for ip_version in versions.iter() { let filename = format!( "test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb", ip_version, record_size ); let reader = Reader::open_mmap(filename).ok().unwrap(); check_metadata(&reader, *ip_version, *record_size); check_ip(&reader, *ip_version); } } } #[test] fn test_lookup_city() { use super::geoip2::City; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-City-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip: IpAddr = FromStr::from_str("89.160.20.112").unwrap(); let city: City = reader.lookup(ip).unwrap(); let iso_code = city.country.and_then(|cy| cy.iso_code); assert_eq!(iso_code, Some("SE")); } #[test] fn test_lookup_country() { use super::geoip2::Country; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-Country-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip: IpAddr = FromStr::from_str("89.160.20.112").unwrap(); let country: Country = reader.lookup(ip).unwrap(); let country = country.country.unwrap(); assert_eq!(country.iso_code, Some("SE")); assert_eq!(country.is_in_european_union, Some(true)); } #[test] fn test_lookup_connection_type() { use super::geoip2::ConnectionType; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-Connection-Type-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip: IpAddr = FromStr::from_str("96.1.20.112").unwrap(); let connection_type: ConnectionType = reader.lookup(ip).unwrap(); assert_eq!(connection_type.connection_type, Some("Cable/DSL")); } #[test] fn test_lookup_annonymous_ip() { use super::geoip2::AnonymousIp; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-Anonymous-IP-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip: IpAddr = FromStr::from_str("81.2.69.123").unwrap(); let anonymous_ip: AnonymousIp = reader.lookup(ip).unwrap(); assert_eq!(anonymous_ip.is_anonymous, Some(true)); assert_eq!(anonymous_ip.is_public_proxy, Some(true)); assert_eq!(anonymous_ip.is_anonymous_vpn, Some(true)); assert_eq!(anonymous_ip.is_hosting_provider, Some(true)); assert_eq!(anonymous_ip.is_tor_exit_node, Some(true)) } #[test] fn test_lookup_density_income() { use super::geoip2::DensityIncome; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-DensityIncome-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip: IpAddr = FromStr::from_str("5.83.124.123").unwrap(); let density_income: DensityIncome = reader.lookup(ip).unwrap(); assert_eq!(density_income.average_income, Some(32323)); assert_eq!(density_income.population_density, Some(1232)) } #[test] fn test_lookup_domain() { use super::geoip2::Domain; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-Domain-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip: IpAddr = FromStr::from_str("66.92.80.123").unwrap(); let domain: Domain = reader.lookup(ip).unwrap(); assert_eq!(domain.domain, Some("speakeasy.net")); } #[test] fn test_lookup_isp() { use super::geoip2::Isp; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-ISP-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip: IpAddr = FromStr::from_str("12.87.118.123").unwrap(); let isp: Isp = reader.lookup(ip).unwrap(); assert_eq!(isp.autonomous_system_number, Some(7018)); assert_eq!(isp.isp, Some("AT&T Services")); assert_eq!(isp.organization, Some("AT&T Worldnet Services")); } #[test] fn test_lookup_asn() { use super::geoip2::Asn; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-ISP-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip: IpAddr = FromStr::from_str("1.128.0.123").unwrap(); let asn: Asn = reader.lookup(ip).unwrap(); assert_eq!(asn.autonomous_system_number, Some(1221)); assert_eq!(asn.autonomous_system_organization, Some("Telstra Pty Ltd")); } #[test] fn test_lookup_prefix() { use super::geoip2::City; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-ISP-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); // IPv4 let ip: IpAddr = "89.160.20.128".parse().unwrap(); let (_, prefix_len) = reader.lookup_prefix::(ip).unwrap(); assert_eq!(prefix_len, 25); // "::89.160.20.128/121" // Last host let ip: IpAddr = "89.160.20.254".parse().unwrap(); let (_, last_prefix_len) = reader.lookup_prefix::(ip).unwrap(); assert_eq!(prefix_len, last_prefix_len); // IPv6 let ip: IpAddr = "2c0f:ff00::1".parse().unwrap(); let (_, prefix_len) = reader.lookup_prefix::(ip).unwrap(); assert_eq!(prefix_len, 26); // "2c0f:ff00::/26" } #[test] fn test_within_city() { use super::geoip2::City; use super::Within; use ipnetwork::IpNetwork; let _ = env_logger::try_init(); let filename = "test-data/test-data/GeoIP2-City-Test.mmdb"; let reader = Reader::open_readfile(filename).unwrap(); let ip_net = IpNetwork::V6("::/0".parse().unwrap()); let mut iter: Within = reader.within(ip_net).unwrap(); // Make sure the first is what we expect it to be let item = iter.next().unwrap().unwrap(); assert_eq!(item.ip_net, IpNetwork::V4("2.2.3.0/24".parse().unwrap())); assert_eq!(item.info.city.unwrap().geoname_id, Some(2_655_045)); let mut n = 1; for _ in iter { n += 1; } // Make sure we had the expected number assert_eq!(n, 243); // A second run through this time a specific network let specific = IpNetwork::V4("81.2.69.0/24".parse().unwrap()); let mut iter: Within = reader.within(specific).unwrap(); // Make sure we have the expected blocks/info let mut expected = vec![ // Note: reversed so we can use pop IpNetwork::V4("81.2.69.192/28".parse().unwrap()), IpNetwork::V4("81.2.69.160/27".parse().unwrap()), IpNetwork::V4("81.2.69.144/28".parse().unwrap()), IpNetwork::V4("81.2.69.142/31".parse().unwrap()), ]; while let Some(e) = expected.pop() { let item = iter.next().unwrap().unwrap(); assert_eq!(item.ip_net, e); } } fn check_metadata>(reader: &Reader, ip_version: usize, record_size: usize) { let metadata = &reader.metadata; assert_eq!(metadata.binary_format_major_version, 2_u16); assert_eq!(metadata.binary_format_minor_version, 0_u16); assert!(metadata.build_epoch >= 1_397_457_605); assert_eq!(metadata.database_type, "Test".to_string()); assert_eq!( *metadata.description[&"en".to_string()], "Test Database".to_string() ); assert_eq!( *metadata.description[&"zh".to_string()], "Test Database Chinese".to_string() ); assert_eq!(metadata.ip_version, ip_version as u16); assert_eq!(metadata.languages, vec!["en".to_string(), "zh".to_string()]); if ip_version == 4 { assert_eq!(metadata.node_count, 164) } else { assert_eq!(metadata.node_count, 416) } assert_eq!(metadata.record_size, record_size as u16) } fn check_ip>(reader: &Reader, ip_version: usize) { let subnets = match ip_version { 6 => [ "::1:ffff:ffff", "::2:0:0", "::2:0:0", "::2:0:0", "::2:0:0", "::2:0:40", "::2:0:40", "::2:0:40", "::2:0:50", "::2:0:50", "::2:0:50", "::2:0:58", "::2:0:58", ], _ => [ "1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.4", "1.1.1.4", "1.1.1.4", "1.1.1.4", "1.1.1.8", "1.1.1.8", "1.1.1.8", "1.1.1.16", "1.1.1.16", "1.1.1.16", ], }; #[derive(Deserialize, Debug)] struct IpType { ip: String, } for subnet in &subnets { let ip: IpAddr = FromStr::from_str(subnet).unwrap(); let value: IpType = reader.lookup(ip).unwrap(); assert_eq!(value.ip, *subnet); } let no_record = ["1.1.1.33", "255.254.253.123", "89fa::"]; for &address in &no_record { let ip: IpAddr = FromStr::from_str(address).unwrap(); match reader.lookup::(ip) { Ok(v) => panic!("received an unexpected value: {v:?}"), Err(e) => assert_eq!( e, MaxMindDBError::AddressNotFoundError("Address not found in database".to_string()) ), } } }