Skip to content

Commit 685f511

Browse files
authored
Merge pull request #26 from vavrusa/master
Add `lookup_prefix` that also returns the prefix length for the node
2 parents 4b6005c + 12557e2 commit 685f511

File tree

2 files changed

+59
-5
lines changed

2 files changed

+59
-5
lines changed

src/maxminddb/lib.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -261,20 +261,43 @@ impl<'de, S: AsRef<[u8]>> Reader<S> {
261261
/// print!("{:?}", city);
262262
/// ```
263263
pub fn lookup<T>(&'de self, address: IpAddr) -> Result<T, MaxMindDBError>
264+
where
265+
T: Deserialize<'de>,
266+
{
267+
self.lookup_prefix(address).map(|(v, _)| v)
268+
}
269+
270+
/// Lookup the socket address in the opened MaxMind DB
271+
///
272+
/// Example:
273+
///
274+
/// ```
275+
/// use maxminddb::geoip2;
276+
/// use std::net::IpAddr;
277+
/// use std::str::FromStr;
278+
///
279+
/// let reader = maxminddb::Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
280+
///
281+
/// let ip: IpAddr = "89.160.20.128".parse().unwrap();
282+
/// let (city, prefix_len) = reader.lookup_prefix::<geoip2::City>(ip).unwrap();
283+
/// print!("{:?}, prefix length: {}", city, prefix_len);
284+
/// ```
285+
pub fn lookup_prefix<T>(&'de self, address: IpAddr) -> Result<(T, usize), MaxMindDBError>
264286
where
265287
T: Deserialize<'de>,
266288
{
267289
let ip_bytes = ip_to_bytes(address);
268-
let pointer = self.find_address_in_tree(&ip_bytes)?;
290+
let (pointer, prefix_len) = self.find_address_in_tree(&ip_bytes)?;
269291
if pointer == 0 {
270292
return Err(MaxMindDBError::AddressNotFoundError(
271293
"Address not found in database".to_owned(),
272294
));
273295
}
296+
274297
let rec = self.resolve_data_pointer(pointer)?;
275298
let mut decoder = decoder::Decoder::new(&self.buf.as_ref()[self.pointer_base..], rec);
276299

277-
T::deserialize(&mut decoder)
300+
T::deserialize(&mut decoder).map(|v| (v, prefix_len))
278301
}
279302

280303
/// Iterate over blocks of IP networks in the opened MaxMind DB
@@ -343,23 +366,25 @@ impl<'de, S: AsRef<[u8]>> Reader<S> {
343366
Ok(within)
344367
}
345368

346-
fn find_address_in_tree(&self, ip_address: &[u8]) -> Result<usize, MaxMindDBError> {
369+
fn find_address_in_tree(&self, ip_address: &[u8]) -> Result<(usize, usize), MaxMindDBError> {
347370
let bit_count = ip_address.len() * 8;
348371
let mut node = self.start_node(bit_count);
349372

350373
let node_count = self.metadata.node_count as usize;
374+
let mut prefix_len = bit_count;
351375

352376
for i in 0..bit_count {
353377
if node >= node_count {
378+
prefix_len = i;
354379
break;
355380
}
356381
let bit = 1 & (ip_address[i >> 3] >> (7 - (i % 8)));
357382

358383
node = self.read_node(node, bit as usize)?;
359384
}
360385
match node_count {
361-
n if n == node => Ok(0),
362-
n if node > n => Ok(node),
386+
n if n == node => Ok((0, prefix_len)),
387+
n if node > n => Ok((node, prefix_len)),
363388
_ => Err(MaxMindDBError::InvalidDatabaseError(
364389
"invalid node in search tree".to_owned(),
365390
)),
@@ -455,6 +480,7 @@ fn ip_to_bytes(address: IpAddr) -> Vec<u8> {
455480
}
456481
}
457482

483+
#[allow(clippy::many_single_char_names)]
458484
fn bytes_and_prefix_to_net(bytes: &[u8], prefix: u8) -> Result<IpNetwork, MaxMindDBError> {
459485
let (ip, pre) = match bytes.len() {
460486
4 => (

src/maxminddb/reader_test.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,34 @@ fn test_lookup_asn() {
335335
assert_eq!(asn.autonomous_system_organization, Some("Telstra Pty Ltd"));
336336
}
337337

338+
#[test]
339+
fn test_lookup_prefix() {
340+
use super::geoip2::City;
341+
let _ = env_logger::try_init();
342+
343+
let filename = "test-data/test-data/GeoIP2-ISP-Test.mmdb";
344+
345+
let reader = Reader::open_readfile(filename).unwrap();
346+
347+
// IPv4
348+
let ip: IpAddr = "89.160.20.128".parse().unwrap();
349+
let (_, prefix_len) = reader.lookup_prefix::<City>(ip).unwrap();
350+
351+
assert_eq!(prefix_len, 25); // "::89.160.20.128/121"
352+
353+
// Last host
354+
let ip: IpAddr = "89.160.20.254".parse().unwrap();
355+
let (_, last_prefix_len) = reader.lookup_prefix::<City>(ip).unwrap();
356+
357+
assert_eq!(prefix_len, last_prefix_len);
358+
359+
// IPv6
360+
let ip: IpAddr = "2c0f:ff00::1".parse().unwrap();
361+
let (_, prefix_len) = reader.lookup_prefix::<City>(ip).unwrap();
362+
363+
assert_eq!(prefix_len, 26); // "2c0f:ff00::/26"
364+
}
365+
338366
#[test]
339367
fn test_within_city() {
340368
use super::geoip2::City;

0 commit comments

Comments
 (0)