fetch_ccip/
parse.rs

1use crate::parse_ipv4::largest_ipv4_block;
2use ipnet::{IpNet, Ipv4Net, Ipv6Net};
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        // コメント行やreserved行をスキップ
16        if line.starts_with('#') || line.contains('*') || line.contains("reserved") {
17            continue;
18        }
19
20        let params: Vec<&str> = line.split('|').collect();
21        if params.len() < 5 {
22            continue;
23        }
24
25        // 国コードフィルタ
26        if params[1] != country_code {
27            continue;
28        }
29
30        let ip_type = params[2];
31        match ip_type {
32            "ipv4" | "ipv6" => match parse_ip_params(&params) {
33                Ok(nets) => {
34                    if ip_type == "ipv4" {
35                        ipv4_list.extend(nets);
36                    } else {
37                        ipv6_list.extend(nets);
38                    }
39                }
40                Err(e) => {
41                    eprintln!("[parse_ip_lines] parse_ip_params error: {}", e);
42                }
43            },
44            _ => continue,
45        }
46    }
47
48    Ok((ipv4_list, ipv6_list))
49}
50
51/// ip_typeを判別し、対応するパース関数を呼び出す。
52fn parse_ip_params(
53    params: &[&str],
54) -> Result<Vec<IpNet>, Box<dyn std::error::Error + Send + Sync>> {
55    let ip_type = params[2];
56    let start_str = params[3];
57    let value_str = params[4];
58
59    match ip_type {
60        "ipv4" => parse_ipv4_range(start_str, value_str),
61        "ipv6" => parse_ipv6_range(start_str, value_str),
62        _ => Ok(vec![]),
63    }
64}
65
66/// IPv4の範囲を細分化してCIDRブロック一覧を返す。
67fn parse_ipv4_range(
68    start_str: &str,
69    value_str: &str,
70) -> Result<Vec<IpNet>, Box<dyn std::error::Error + Send + Sync>> {
71    let start_v4 = start_str.parse::<Ipv4Addr>()?;
72    let width = value_str.parse::<u64>()?;
73    let start_num = u32::from(start_v4);
74
75    let end_num = start_num
76        .checked_add(width as u32)
77        .ok_or("IPv4 range is too large")?
78        .checked_sub(1)
79        .ok_or("Calculation error on IPv4 range")?;
80
81    let mut cidrs = Vec::new();
82    let mut current = start_num;
83
84    while current <= end_num {
85        let max_size = largest_ipv4_block(current, end_num);
86        let net = Ipv4Net::new(Ipv4Addr::from(current), max_size)?;
87        cidrs.push(IpNet::V4(net));
88
89        let block_size = 1u32 << (32 - max_size);
90        current = current.saturating_add(block_size);
91    }
92
93    Ok(cidrs)
94}
95
96/// IPv6用のCIDRをパースして返す。
97fn parse_ipv6_range(
98    start_str: &str,
99    value_str: &str,
100) -> Result<Vec<IpNet>, Box<dyn std::error::Error + Send + Sync>> {
101    // RIRのフォーマットでは`start_str/value_str`が基本
102    let cidr_str = format!("{}/{}", start_str, value_str);
103    let net = cidr_str.parse::<Ipv6Net>()?;
104    Ok(vec![IpNet::V6(net)])
105}