maxminddb/
within.rs

1//! Network iteration types.
2
3use std::cmp::Ordering;
4use std::net::IpAddr;
5
6use crate::decoder;
7use crate::error::MaxMindDbError;
8use crate::reader::Reader;
9use crate::result::LookupResult;
10
11/// Options for network iteration.
12///
13/// Controls which networks are yielded when iterating over the database
14/// with [`Reader::within()`] or [`Reader::networks()`].
15///
16/// # Example
17///
18/// ```
19/// use maxminddb::WithinOptions;
20///
21/// // Default options (skip aliases, skip networks without data, include empty values)
22/// let opts = WithinOptions::default();
23///
24/// // Include aliased networks (IPv4 networks via IPv6 aliases)
25/// let opts = WithinOptions::default().include_aliased_networks();
26///
27/// // Skip empty values and include networks without data
28/// let opts = WithinOptions::default()
29///     .skip_empty_values()
30///     .include_networks_without_data();
31/// ```
32#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
33pub struct WithinOptions {
34    /// Include IPv4 networks multiple times when accessed via IPv6 aliases.
35    include_aliased_networks: bool,
36    /// Include networks that have no associated data record.
37    include_networks_without_data: bool,
38    /// Skip networks whose data is an empty map or empty array.
39    skip_empty_values: bool,
40}
41
42impl WithinOptions {
43    /// Include IPv4 networks multiple times when accessed via IPv6 aliases.
44    ///
45    /// In IPv6 databases, IPv4 networks are stored at `::0/96`. However, the
46    /// same data is accessible through several IPv6 prefixes (e.g.,
47    /// `::ffff:0:0/96` for IPv4-mapped IPv6). By default, these aliases are
48    /// skipped to avoid yielding the same network multiple times.
49    ///
50    /// When enabled, the iterator will yield these aliased networks.
51    #[must_use]
52    pub fn include_aliased_networks(mut self) -> Self {
53        self.include_aliased_networks = true;
54        self
55    }
56
57    /// Include networks that have no associated data record.
58    ///
59    /// Some tree nodes point to "no data" (the node_count sentinel). By default
60    /// these are skipped. When enabled, these networks are yielded and
61    /// [`LookupResult::has_data()`] returns `false` for them.
62    #[must_use]
63    pub fn include_networks_without_data(mut self) -> Self {
64        self.include_networks_without_data = true;
65        self
66    }
67
68    /// Skip networks whose data is an empty map or empty array.
69    ///
70    /// Some databases store empty maps `{}` or empty arrays `[]` for records
71    /// without meaningful data. This option filters them out.
72    #[must_use]
73    pub fn skip_empty_values(mut self) -> Self {
74        self.skip_empty_values = true;
75        self
76    }
77}
78
79#[derive(Debug)]
80pub(crate) struct WithinNode {
81    pub(crate) node: usize,
82    pub(crate) ip_int: IpInt,
83    pub(crate) prefix_len: usize,
84}
85
86/// Iterator over IP networks within a CIDR range.
87///
88/// Created by [`Reader::within()`](crate::Reader::within) or
89/// [`Reader::networks()`](crate::Reader::networks). Yields
90/// [`LookupResult`] for each network in the database that falls
91/// within the specified range.
92///
93/// Networks are yielded in depth-first order through the search tree.
94/// Use [`LookupResult::decode()`](crate::LookupResult::decode) to
95/// deserialize the data for each result.
96///
97/// # Example
98///
99/// ```
100/// use maxminddb::{Reader, WithinOptions, geoip2};
101///
102/// let reader = Reader::open_readfile("test-data/test-data/GeoIP2-City-Test.mmdb").unwrap();
103/// for result in reader.within("89.160.20.0/24".parse().unwrap(), Default::default()).unwrap() {
104///     let lookup = result.unwrap();
105///     if let Some(city) = lookup.decode::<geoip2::City>().unwrap() {
106///         println!("{}: {:?}", lookup.network().unwrap(), city.city.names.english);
107///     }
108/// }
109/// ```
110#[derive(Debug)]
111pub struct Within<'de, S: AsRef<[u8]>> {
112    pub(crate) reader: &'de Reader<S>,
113    pub(crate) node_count: usize,
114    pub(crate) stack: Vec<WithinNode>,
115    pub(crate) options: WithinOptions,
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub(crate) enum IpInt {
120    V4(u32),
121    V6(u128),
122}
123
124impl IpInt {
125    pub(crate) fn new(ip_addr: IpAddr) -> Self {
126        match ip_addr {
127            IpAddr::V4(v4) => IpInt::V4(v4.into()),
128            IpAddr::V6(v6) => IpInt::V6(v6.into()),
129        }
130    }
131
132    #[inline(always)]
133    pub(crate) fn get_bit(&self, index: usize) -> bool {
134        match self {
135            IpInt::V4(ip) => (ip >> (31 - index)) & 1 == 1,
136            IpInt::V6(ip) => (ip >> (127 - index)) & 1 == 1,
137        }
138    }
139
140    pub(crate) fn bit_count(&self) -> usize {
141        match self {
142            IpInt::V4(_) => 32,
143            IpInt::V6(_) => 128,
144        }
145    }
146
147    pub(crate) fn is_ipv4_in_ipv6(&self) -> bool {
148        match self {
149            IpInt::V4(_) => false,
150            IpInt::V6(ip) => *ip <= 0xFFFFFFFF,
151        }
152    }
153}
154
155impl<'de, S: AsRef<[u8]>> Iterator for Within<'de, S> {
156    type Item = Result<LookupResult<'de, S>, MaxMindDbError>;
157
158    fn next(&mut self) -> Option<Self::Item> {
159        while let Some(current) = self.stack.pop() {
160            let bit_count = current.ip_int.bit_count();
161
162            // Skip networks that are aliases for the IPv4 network (unless option is set)
163            if !self.options.include_aliased_networks
164                && self.reader.ipv4_start != 0
165                && current.node == self.reader.ipv4_start
166                && bit_count == 128
167                && !current.ip_int.is_ipv4_in_ipv6()
168            {
169                continue;
170            }
171
172            match current.node.cmp(&self.node_count) {
173                Ordering::Greater => {
174                    // This is a data node, emit it and we're done (until the following next call)
175                    let ip_addr = ip_int_to_addr(&current.ip_int);
176
177                    // Resolve the pointer to a data offset
178                    let data_offset = match self.reader.resolve_data_pointer(current.node) {
179                        Ok(offset) => offset,
180                        Err(e) => return Some(Err(e)),
181                    };
182
183                    // Check if we should skip empty values
184                    if self.options.skip_empty_values {
185                        match self.is_empty_value_at(data_offset) {
186                            Ok(true) => continue, // Skip empty value
187                            Ok(false) => {}       // Not empty, proceed
188                            Err(e) => return Some(Err(e)),
189                        }
190                    }
191
192                    return Some(Ok(LookupResult::new_found(
193                        self.reader,
194                        data_offset,
195                        current.prefix_len as u8,
196                        ip_addr,
197                    )));
198                }
199                Ordering::Equal => {
200                    // Dead end (no data) - include if option is set
201                    if self.options.include_networks_without_data {
202                        let ip_addr = ip_int_to_addr(&current.ip_int);
203                        return Some(Ok(LookupResult::new_not_found(
204                            self.reader,
205                            current.prefix_len as u8,
206                            ip_addr,
207                        )));
208                    }
209                    // Otherwise skip (current behavior)
210                }
211                Ordering::Less => {
212                    // In order traversal of our children
213                    // right/1-bit
214                    let mut right_ip_int = current.ip_int;
215
216                    if current.prefix_len < bit_count {
217                        let bit = current.prefix_len;
218                        match &mut right_ip_int {
219                            IpInt::V4(ip) => *ip |= 1 << (31 - bit),
220                            IpInt::V6(ip) => *ip |= 1 << (127 - bit),
221                        };
222                    }
223
224                    let node = match self.reader.read_node(current.node, 1) {
225                        Ok(node) => node,
226                        Err(e) => return Some(Err(e)),
227                    };
228                    self.stack.push(WithinNode {
229                        node,
230                        ip_int: right_ip_int,
231                        prefix_len: current.prefix_len + 1,
232                    });
233                    // left/0-bit
234                    let node = match self.reader.read_node(current.node, 0) {
235                        Ok(node) => node,
236                        Err(e) => return Some(Err(e)),
237                    };
238                    self.stack.push(WithinNode {
239                        node,
240                        ip_int: current.ip_int,
241                        prefix_len: current.prefix_len + 1,
242                    });
243                }
244            }
245        }
246        None
247    }
248}
249
250impl<'de, S: AsRef<[u8]>> Within<'de, S> {
251    /// Check if the value at the given data offset is an empty map or array.
252    fn is_empty_value_at(&self, data_offset: usize) -> Result<bool, MaxMindDbError> {
253        let buf = &self.reader.buf.as_ref()[self.reader.pointer_base..];
254        let mut dec = decoder::Decoder::new(buf, data_offset);
255        let (size, type_num) = dec.peek_type()?;
256        match type_num {
257            decoder::TYPE_MAP | decoder::TYPE_ARRAY => Ok(size == 0),
258            _ => Ok(false), // Non-container types are never "empty"
259        }
260    }
261}
262
263/// Convert IpInt to IpAddr
264pub(crate) fn ip_int_to_addr(ip_int: &IpInt) -> IpAddr {
265    match ip_int {
266        IpInt::V4(ip) => IpAddr::V4((*ip).into()),
267        IpInt::V6(ip) => {
268            // Check if this is an IPv4-mapped IPv6 address
269            if *ip <= 0xFFFFFFFF {
270                IpAddr::V4((*ip as u32).into())
271            } else {
272                IpAddr::V6((*ip).into())
273            }
274        }
275    }
276}