fire_scope/
parse.rs

1use ipnet::{IpNet, Ipv4Net, Ipv6Net};
2use std::collections::HashMap;
3use std::net::Ipv4Addr;
4
5/// RIR が提供するテキストデータを行ごとに解析し、
6/// 指定された`country_code`に合致する IPv4/IPv6 のリストを返す。
7pub fn parse_ip_lines(
8    text: &str,
9    country_code: &str,
10) -> Result<(Vec<IpNet>, Vec<IpNet>), Box<dyn std::error::Error + Send + Sync>> {
11    let mut ipv4_list = Vec::new();
12    let mut ipv6_list = Vec::new();
13
14    for line in text.lines() {
15        if line.starts_with('#') || line.contains('*') || line.contains("reserved") {
16            continue;
17        }
18
19        let params: Vec<&str> = line.split('|').collect();
20        if params.len() < 5 {
21            continue;
22        }
23
24        if params[1].eq_ignore_ascii_case(country_code) {
25            let ip_type = params[2];
26            match ip_type {
27                "ipv4" | "ipv6" => match parse_ip_params(&params) {
28                    Ok(nets) => {
29                        if ip_type == "ipv4" {
30                            ipv4_list.extend(nets);
31                        } else {
32                            ipv6_list.extend(nets);
33                        }
34                    }
35                    Err(e) => eprintln!("[parse_ip_lines] parse_ip_params error: {}", e),
36                },
37                _ => continue,
38            }
39        }
40    }
41
42    Ok((ipv4_list, ipv6_list))
43}
44
45/// ip_typeを判別し、対応するパース関数を呼び出す。
46fn parse_ip_params(
47    params: &[&str],
48) -> Result<Vec<IpNet>, Box<dyn std::error::Error + Send + Sync>> {
49    let ip_type = params[2];
50    let start_str = params[3];
51    let value_str = params[4];
52
53    match ip_type {
54        "ipv4" => parse_ipv4_range(start_str, value_str),
55        "ipv6" => parse_ipv6_range(start_str, value_str),
56        _ => Ok(vec![]),
57    }
58}
59
60/// IPv4の範囲を細分化してCIDRブロック一覧を返す。
61fn parse_ipv4_range(
62    start_str: &str,
63    value_str: &str,
64) -> Result<Vec<IpNet>, Box<dyn std::error::Error + Send + Sync>> {
65    let start_v4 = start_str.parse::<Ipv4Addr>()?;
66    let width = value_str.parse::<u64>()?;
67    let start_num = u32::from(start_v4);
68
69    let end_num = start_num
70        .checked_add(width as u32)
71        .ok_or("IPv4 range is too large")?
72        .checked_sub(1)
73        .ok_or("Calculation error on IPv4 range")?;
74
75    let mut cidrs = Vec::new();
76    let mut current = start_num;
77
78    // ここで parse_ipv4::largest_ipv4_block を呼ぶか、
79    // あるいは ipv4_utils::largest_ipv4_block に切り替えてもOK。
80    while current <= end_num {
81        let max_size = crate::ipv4_utils::largest_ipv4_block(current, end_num);
82        let net = Ipv4Net::new(Ipv4Addr::from(current), max_size)?;
83        cidrs.push(IpNet::V4(net));
84
85        let block_size = 1u32 << (32 - max_size);
86        current = current.saturating_add(block_size);
87    }
88
89    Ok(cidrs)
90}
91
92/// IPv6用のCIDRをパースして返す。
93fn parse_ipv6_range(
94    start_str: &str,
95    value_str: &str,
96) -> Result<Vec<IpNet>, Box<dyn std::error::Error + Send + Sync>> {
97    let cidr_str = format!("{}/{}", start_str, value_str);
98    let net = cidr_str.parse::<Ipv6Net>()?;
99    Ok(vec![IpNet::V6(net)])
100}
101
102/// 全RIRテキストから全ての国コードに対するIPアドレスをパースし、国コードをキーとするマップを返す。
103/// 戻り値: HashMap<国コード, (IPv4リスト, IPv6リスト)>
104type CountryIpMap = HashMap<String, (Vec<IpNet>, Vec<IpNet>)>;
105pub fn parse_all_country_codes(
106    rir_texts: &[String],
107) -> Result<CountryIpMap, Box<dyn std::error::Error + Send + Sync>> {
108    let mut country_map: HashMap<String, (Vec<IpNet>, Vec<IpNet>)> = HashMap::new();
109
110    for text in rir_texts {
111        for line in text.lines() {
112            if line.starts_with('#') || line.contains('*') || line.contains("reserved") {
113                continue;
114            }
115
116            let params: Vec<&str> = line.split('|').collect();
117            if params.len() < 5 {
118                continue;
119            }
120
121            let country_code = params[1].to_uppercase();
122            let ip_type = params[2];
123
124            match ip_type {
125                "ipv4" | "ipv6" => match parse_ip_params(&params) {
126                    Ok(nets) => {
127                        let entry = country_map
128                            .entry(country_code)
129                            .or_insert((Vec::new(), Vec::new()));
130                        if ip_type == "ipv4" {
131                            entry.0.extend(nets);
132                        } else {
133                            entry.1.extend(nets);
134                        }
135                    }
136                    Err(e) => {
137                        eprintln!("[parse_all_country_codes] parse_ip_params error: {}", e)
138                    }
139                },
140                _ => continue,
141            }
142        }
143    }
144
145    Ok(country_map)
146}