fetch_ccip/
parse.rs

1use crate::parse_ipv4::largest_ipv4_block;
2use ipnet::{IpNet, Ipv4Net, Ipv6Net};
3use std::net::Ipv4Addr;
4
5/// テキスト全体を行ごとに分解し、指定された `country_code` に合致するIPv4/IPv6のリストを返す。
6pub fn parse_ip_lines(
7    text: &str,
8    country_code: &str,
9) -> Result<(Vec<IpNet>, Vec<IpNet>), Box<dyn std::error::Error + Send + Sync>> {
10    let mut ipv4_list = Vec::new();
11    let mut ipv6_list = Vec::new();
12    for line in text.lines() {
13        // コメント行やreserved行をスキップ
14        if line.starts_with('#') || line.contains('*') || line.contains("reserved") {
15            continue;
16        }
17        let params: Vec<&str> = line.split('|').collect();
18        if params.len() < 5 {
19            continue;
20        }
21        // 国コードフィルタ
22        if params[1] != country_code {
23            continue;
24        }
25        let ip_type = params[2];
26        if ip_type == "ipv4" || ip_type == "ipv6" {
27            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) => {
36                    eprintln!("parse_ip_paramsでエラー: {}", e);
37                }
38            }
39        }
40    }
41    Ok((ipv4_list, ipv6_list))
42}
43
44/// ip_typeを判別し、対応するパース関数を呼び出す
45fn parse_ip_params(
46    params: &[&str],
47) -> Result<Vec<IpNet>, Box<dyn std::error::Error + Send + Sync>> {
48    let ip_type = params[2];
49    let start_str = params[3];
50    let value_str = params[4];
51    if ip_type == "ipv4" {
52        parse_ipv4(start_str, value_str)
53    } else if ip_type == "ipv6" {
54        parse_ipv6(start_str, value_str)
55    } else {
56        Ok(vec![])
57    }
58}
59
60/// IPv4をパースし、必要に応じてCIDRブロックに細分化する
61fn parse_ipv4(
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("範囲が大きすぎます")?
72        .checked_sub(1)
73        .ok_or("計算エラー")?;
74
75    let mut cidrs = Vec::new();
76    let mut current = start_num;
77    while current <= end_num {
78        let max_size = largest_ipv4_block(current, end_num);
79        let net = Ipv4Net::new(Ipv4Addr::from(current), max_size)?;
80        cidrs.push(IpNet::V4(net));
81
82        let block_size = 1u32 << (32 - max_size);
83        current = current.saturating_add(block_size);
84    }
85    Ok(cidrs)
86}
87
88/// IPv6をパースする
89fn parse_ipv6(
90    start_str: &str,
91    value_str: &str,
92) -> Result<Vec<IpNet>, Box<dyn std::error::Error + Send + Sync>> {
93    // RIRのフォーマット上、IPv6は「prefix/length」形式で丸ごとを扱う
94    let cidr_str = format!("{}/{}", start_str, value_str);
95    let net = cidr_str.parse::<Ipv6Net>()?;
96    Ok(vec![IpNet::V6(net)])
97}