Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ripcalc for openSUSE:Factory checked in at 2026-08-18 16:37:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ripcalc (Old) and /work/SRC/openSUSE:Factory/.ripcalc.new.1258 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ripcalc" Tue Aug 18 16:37:04 2026 rev:7 rq:1371631 version:0.4.0 Changes: -------- --- /work/SRC/openSUSE:Factory/ripcalc/ripcalc.changes 2026-05-26 16:34:08.072251557 +0200 +++ /work/SRC/openSUSE:Factory/.ripcalc.new.1258/ripcalc.changes 2026-08-18 16:37:48.213765948 +0200 @@ -1,0 +2,8 @@ +Sun Aug 16 20:05:37 UTC 2026 - Heitor Moreira da Silva <[email protected]> + +- Update to version 0.4.0: + * feat: ula random ipv6 + * keepdata option + * tidyup function definitions + +------------------------------------------------------------------- Old: ---- ripcalc-0.3.1.obscpio New: ---- ripcalc-0.4.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ripcalc.spec ++++++ --- /var/tmp/diff_new_pack.fh1SLu/_old 2026-08-18 16:37:50.846860176 +0200 +++ /var/tmp/diff_new_pack.fh1SLu/_new 2026-08-18 16:37:50.851860355 +0200 @@ -18,7 +18,7 @@ Name: ripcalc -Version: 0.3.1 +Version: 0.4.0 Release: 0 Summary: Tool for network addresses License: GPL-3.0-or-later ++++++ _service ++++++ --- /var/tmp/diff_new_pack.fh1SLu/_old 2026-08-18 16:37:50.937863433 +0200 +++ /var/tmp/diff_new_pack.fh1SLu/_new 2026-08-18 16:37:50.944863683 +0200 @@ -5,7 +5,7 @@ <param name="changesgenerate">enable</param> <param name="filename">ripcalc</param> <param name="versionformat">@PARENT_TAG@</param> - <param name="revision">v0.3.1</param> + <param name="revision">v0.4.0</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="versionrewrite-replacement">\1</param> </service> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.fh1SLu/_old 2026-08-18 16:37:50.990865329 +0200 +++ /var/tmp/diff_new_pack.fh1SLu/_new 2026-08-18 16:37:51.002865759 +0200 @@ -1,7 +1,7 @@ <servicedata> <service name="tar_scm"> <param name="url">https://gitlab.com/edneville/ripcalc.git</param> - <param name="changesrevision">d206b49b3b1447102c9220fd3e2a66be8c68fcb9</param> + <param name="changesrevision">279ea7fdc5c3b033f4ed4b9d3d210896516b45c8</param> </service> </servicedata> (No newline at EOF) ++++++ ripcalc-0.3.1.obscpio -> ripcalc-0.4.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ripcalc-0.3.1/Cargo.lock new/ripcalc-0.4.0/Cargo.lock --- old/ripcalc-0.3.1/Cargo.lock 2026-05-21 21:26:33.000000000 +0200 +++ new/ripcalc-0.4.0/Cargo.lock 2026-07-22 22:33:13.000000000 +0200 @@ -901,6 +901,15 @@ ] [[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -925,6 +934,36 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1011,7 +1050,7 @@ [[package]] name = "ripcalc" -version = "0.3.1" +version = "0.4.0" dependencies = [ "cdb", "chrono", @@ -1020,6 +1059,7 @@ "getopts", "maxminddb", "nix", + "rand", "regex", "reqwest", "serde_json", @@ -1831,6 +1871,26 @@ ] [[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ripcalc-0.3.1/Cargo.toml new/ripcalc-0.4.0/Cargo.toml --- old/ripcalc-0.3.1/Cargo.toml 2026-05-21 21:26:33.000000000 +0200 +++ new/ripcalc-0.4.0/Cargo.toml 2026-07-22 22:33:13.000000000 +0200 @@ -1,7 +1,7 @@ [package] name = "ripcalc" description = "ripcalc, format and lookup IP addresses" -version = "0.3.1" +version = "0.4.0" authors = ["ed neville <[email protected]>"] edition = "2018" license = "GPL-3.0-or-later" @@ -25,3 +25,4 @@ serde_json = "1" chrono = "0.4" maxminddb = "0.28" +rand = "0.8" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ripcalc-0.3.1/README.md new/ripcalc-0.4.0/README.md --- old/ripcalc-0.3.1/README.md 2026-05-21 21:26:33.000000000 +0200 +++ new/ripcalc-0.4.0/README.md 2026-07-22 22:33:13.000000000 +0200 @@ -413,6 +413,7 @@ --makethymecdb PATH download and build a cdb file, defaults to thyme.apnic.net data + --keepdata keep data files after download --data-raw-table URL or PATH URL/path of raw data --ipv6-raw-table URL or PATH @@ -424,7 +425,7 @@ --iterations NUMBER top iterations, use --consume to produce whole report of stdin - --noclear don't print clear screen codes + --json PATH json prefix file --mmlang LANG the lang to use in results, defaults to en --mmasn PATH path to maxmind geoip asn file --mmcity PATH path to maxmind geoip city file @@ -433,10 +434,11 @@ -m, --mask CIDR cidr mask -n, --networks CIDR instead of hosts, display number of subnets of this size - --json PATH json prefix file + --noclear don't print clear screen codes -q, --quiet don't report errors -r, --reverse (none, inputs, sources or both) v4 octets, v6 hex -s, --file PATH lookup addresses from, - for stdin + --ula generate pseudorandom IPv6 ULA address -v, --version print version ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ripcalc-0.3.1/changelog.md new/ripcalc-0.4.0/changelog.md --- old/ripcalc-0.3.1/changelog.md 2026-05-21 21:26:33.000000000 +0200 +++ new/ripcalc-0.4.0/changelog.md 2026-07-22 22:33:13.000000000 +0200 @@ -1,3 +1,8 @@ +0.4.0 + + * --keepdata to store downloaded data + * --ula for a pseudorandom IPv6 + 0.3.1 * --filter should assume --inside unless told otherwise diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ripcalc-0.3.1/ripcalc.1 new/ripcalc-0.4.0/ripcalc.1 --- old/ripcalc-0.3.1/ripcalc.1 2026-05-21 21:26:33.000000000 +0200 +++ new/ripcalc-0.4.0/ripcalc.1 2026-07-22 22:33:13.000000000 +0200 @@ -1,7 +1,7 @@ '\" t .\" Automatically generated by Pandoc 3.7.0.2 .\" -.TH "ripcalc" "1" "21 May 2026" "ripcalc 0.3.1" "User Manual" +.TH "ripcalc" "1" "22 July 2026" "ripcalc 0.4.0" "User Manual" .SH NAME ripcalc \- a tool for network addresses .SH SYNOPSIS @@ -41,7 +41,8 @@ \f[B]ripcalc \(enmakecdb [PATH]\f[R] .PP \f[B]ripcalc \(enmakethymecdb [PATH] [\-\-data\-raw\-table PATH] -[\-\-ipv6\-raw\-table PATH] [\-\-data\-used\-autnums PATH]\f[R] +[\-\-ipv6\-raw\-table PATH] [\-\-data\-used\-autnums PATH] +[\-\-keepdata]\f[R] .PP \f[B]ripcalc [\-\-mmasn PATH] [\-\-mmcountry PATH] [\-\-mmcity PATH]\f[R] @@ -52,6 +53,8 @@ .PP \f[B]ripcalc \(enfilternum [NUMBER] [CIDR]\f[R] .PP +\f[B]ripcalc \(enula\f[R] +.PP \f[B]ripcalc \-q/\-\-quiet\f[R] .PP \f[B]ripcalc \-h/\-\-help\f[R] @@ -113,6 +116,9 @@ Using \f[CR]\-\-abuseipdb [key]\f[R] can populate \f[CR]%{abuseipdb_var}\f[R] values for \f[CR]\-\-format\f[R] strings, where \f[CR]var\f[R] is the field name in a abuseipdb result structure. +.PP +\f[CR]\-\-ula\f[R] or the \f[CR]%{ula}\f[R] format string will return a +pseudorandom address from \f[CR]fd00::/8\f[R]. .SH CSV Network matches can be returned from a \f[B]CSV\f[R]. .IP @@ -379,5 +385,8 @@ database from \f[CR]https://thyme.apnic.net/\f[R]. Alternatively, if the argument does not start with http, it is treated as file to read instead of download. +.PP +The \f[CR]\-\-keepdata\f[R] option can be used to retain downloaded data +from \f[CR]\-\-makethymecdb\f[R]. .SH AUTHORS Ed Neville (ed\-ripcalc\(ats5h.net). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ripcalc-0.3.1/ripcalc.md new/ripcalc-0.4.0/ripcalc.md --- old/ripcalc-0.3.1/ripcalc.md 2026-05-21 21:26:33.000000000 +0200 +++ new/ripcalc-0.4.0/ripcalc.md 2026-07-22 22:33:13.000000000 +0200 @@ -2,9 +2,9 @@ title: ripcalc section: 1 header: User Manual -footer: ripcalc 0.3.1 +footer: ripcalc 0.4.0 author: Ed Neville ([email protected]) -date: 21 May 2026 +date: 22 July 2026 --- # NAME @@ -45,7 +45,7 @@ **ripcalc --makecdb [PATH]** -**ripcalc --makethymecdb [PATH] [--data-raw-table PATH] [--ipv6-raw-table PATH] [--data-used-autnums PATH]** +**ripcalc --makethymecdb [PATH] [--data-raw-table PATH] [--ipv6-raw-table PATH] [--data-used-autnums PATH] [--keepdata]** **ripcalc [--mmasn PATH] [--mmcountry PATH] [--mmcity PATH]** @@ -55,6 +55,8 @@ **ripcalc --filternum [NUMBER] [CIDR]** +**ripcalc --ula** + **ripcalc -q/--quiet** **ripcalc -h/--help** @@ -89,6 +91,8 @@ Using `--abuseipdb [key]` can populate `%{abuseipdb_var}` values for `--format` strings, where `var` is the field name in a abuseipdb result structure. +`--ula` or the `%{ula}` format string will return a pseudorandom address from `fd00::/8`. + # CSV Network matches can be returned from a **CSV**. @@ -209,3 +213,5 @@ The option `--makethymecdb` can be used to download an IP database from `https://thyme.apnic.net/`. Alternatively, if the argument does not start with http, it is treated as file to read instead of download. +The `--keepdata` option can be used to retain downloaded data from `--makethymecdb`. + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ripcalc-0.3.1/src/lib.rs new/ripcalc-0.4.0/src/lib.rs --- old/ripcalc-0.3.1/src/lib.rs 2026-05-21 21:26:33.000000000 +0200 +++ new/ripcalc-0.4.0/src/lib.rs 2026-07-22 22:33:13.000000000 +0200 @@ -7,6 +7,7 @@ use nix::sys::socket::SockaddrStorage; use nix::sys::stat::fstat; use nix::sys::stat::SFlag; +use rand::Rng; use regex::*; use reqwest::header::HeaderMap; use serde_json::*; @@ -1374,20 +1375,36 @@ None } -pub fn config_option_true(config: &Config, opt: String) -> bool { - config.options.get(&opt).unwrap_or(&"false".to_string()) != "false" +pub fn config_option_true(config: &Config, opt: &str) -> bool { + config.options.get(opt).unwrap_or(&"false".to_string()) != "false" } -fn getnameforlang<'a>(names : &'a maxminddb::geoip2::Names, lang : &str) -> Option<&'a str> { - if lang == "de" { return names.german } - if lang == "en" { return names.english } - if lang == "es" { return names.spanish } - if lang == "fr" { return names.french } - if lang == "jp" { return names.japanese } - if lang == "pt-BR" { return names.brazilian_portuguese } - if lang == "ru" { return names.russian } - if lang == "zh-CN" { return names.simplified_chinese } - return None +fn getnameforlang<'a>(names: &'a maxminddb::geoip2::Names, lang: &str) -> Option<&'a str> { + if lang == "de" { + return names.german; + } + if lang == "en" { + return names.english; + } + if lang == "es" { + return names.spanish; + } + if lang == "fr" { + return names.french; + } + if lang == "jp" { + return names.japanese; + } + if lang == "pt-BR" { + return names.brazilian_portuguese; + } + if lang == "ru" { + return names.russian; + } + if lang == "zh-CN" { + return names.simplified_chinese; + } + None } pub fn geoip_city(config: &RefCell<Config>, ip: &Ip) -> Option<HashMap<String, String>> { @@ -1416,17 +1433,17 @@ let default_lang = "en".to_string(); let lang = config.options.get("mmlang").unwrap_or(&default_lang); - let c = getnameforlang(&city.city.names,lang); + let c = getnameforlang(&city.city.names, lang); if let Some(c) = c { ret.insert("city".to_string(), c.to_string()); } - let c = getnameforlang(&city.continent.names,lang); + let c = getnameforlang(&city.continent.names, lang); if let Some(c) = c { ret.insert("continent".to_string(), c.to_string()); } - let c = getnameforlang(&city.continent.names,lang); + let c = getnameforlang(&city.continent.names, lang); if let Some(c) = c { ret.insert("country".to_string(), c.to_string()); } @@ -1509,24 +1526,26 @@ let f: IpAddr = ip.to_string().parse().unwrap(); - if let Ok(Ok(Some(Some(country)))) = reader.lookup(f).map(|r| r.decode::<Option<geoip2::Country>>()) { + if let Ok(Ok(Some(Some(country)))) = reader + .lookup(f) + .map(|r| r.decode::<Option<geoip2::Country>>()) + { let config = config.borrow(); let default_lang = "en".to_string(); let lang = config.options.get("mmlang").unwrap_or(&default_lang); let c = &country.continent.names; - if let Some(c) = getnameforlang(c,lang) { + if let Some(c) = getnameforlang(c, lang) { ret.insert("continent".to_string(), c.to_string()); } - - let c = getnameforlang(&country.country.names,lang); + let c = getnameforlang(&country.country.names, lang); if let Some(c) = c { ret.insert("country".to_string(), c.to_string()); } - let c = getnameforlang(&country.registered_country.names,lang); + let c = getnameforlang(&country.registered_country.names, lang); if let Some(c) = c { ret.insert("registered_country".to_string(), c.to_string()); } @@ -1536,6 +1555,42 @@ None } +pub fn ula_gen() -> Ip { + let mut rng = rand::thread_rng(); + Ip { + address: ula_subnet_interface(rng.gen_range(0..=65535)), + cidr: 48, + } +} + +pub fn ula_subnet_interface(subnet_id: u16) -> Addr { + let mut rng = rand::thread_rng(); + let mut global_id = [0u8; 5]; + + let _ = rng.try_fill(&mut global_id); + + let ip_bytes: [u8; 16] = [ + 0xfd, + global_id[0], + global_id[1], + global_id[2], + global_id[3], + global_id[4], + (subnet_id >> 8) as u8, + (subnet_id & 0xFF) as u8, + rng.gen_range(0..=255), + rng.gen_range(0..=255), + rng.gen_range(0..=255), + rng.gen_range(0..=255), + rng.gen_range(0..=255), + rng.gen_range(0..=255), + rng.gen_range(0..=255), + rng.gen_range(0..=255), + ]; + + Addr::V6(Ipv6Addr::from(ip_bytes)) +} + pub fn format_details( format_detail: &FormatDetail, formatted: String, @@ -1583,9 +1638,7 @@ } } - if config_option_true(&config.borrow(), "abuseipdb".to_string()) - && reformatted.contains("%{abuseipdb") - { + if config_option_true(&config.borrow(), "abuseipdb") && reformatted.contains("%{abuseipdb") { let key = config .borrow() .options @@ -1616,9 +1669,9 @@ } } - if (config_option_true(&config.borrow(), "mmasn".to_string()) - || config_option_true(&config.borrow(), "mmcity".to_string()) - || config_option_true(&config.borrow(), "mmcountry".to_string())) + if (config_option_true(&config.borrow(), "mmasn") + || config_option_true(&config.borrow(), "mmcity") + || config_option_true(&config.borrow(), "mmcountry")) && (reformatted.contains("%{mmcity") || reformatted.contains("%{mmcountry") || reformatted.contains("%{mmasn")) @@ -1654,6 +1707,10 @@ } } + while reformatted.contains("%{ula}") { + reformatted = reformatted.replacen("%{ula}", &ula_gen().to_string(), 1); + } + if let Some(line) = &format_detail.line { reformatted = reformatted.replace("%{line}", line); } @@ -1757,9 +1814,7 @@ } 'C' => { let config = config.borrow(); - if config_option_true(&config, "countseen".to_string()) - && config.count.is_some() - { + if config_option_true(&config, "countseen") && config.count.is_some() { out_str.push_str(&get_seen_count(&config, ip).to_string()); } else if let Some(used) = &config.used { let count = used.keys().count(); @@ -1933,7 +1988,7 @@ pub fn group_mask(config: &Config, i: &Ip) -> u32 { match i.address { Addr::V4(_) => { - if config_option_true(config, "group4".to_string()) { + if config_option_true(config, "group4") { return config .options .get("group4") @@ -1943,7 +1998,7 @@ } } Addr::V6(_) => { - if config_option_true(config, "group6".to_string()) { + if config_option_true(config, "group6") { return config .options .get("group6") @@ -1954,7 +2009,7 @@ } } - if config_option_true(config, "group".to_string()) { + if config_option_true(config, "group") { return config .options .get("group") @@ -1967,13 +2022,13 @@ } pub fn config_cidr_grouping(config: &Config) -> bool { - config_option_true(config, "group".to_string()) - || config_option_true(config, "group4".to_string()) - || config_option_true(config, "group6".to_string()) + config_option_true(config, "group") + || config_option_true(config, "group4") + || config_option_true(config, "group6") } pub fn increment_seen_count(config: &mut Config, ip: Ip) { - if config_option_true(config, "countseen".to_string()) && config.count.is_some() { + if config_option_true(config, "countseen") && config.count.is_some() { let cidr = group_mask(config, &ip); let net_mask = network(&Ip { @@ -1987,7 +2042,7 @@ } pub fn get_seen_count(config: &Config, i: &Ip) -> usize { - if config_option_true(config, "countseen".to_string()) && config.count.is_some() { + if config_option_true(config, "countseen") && config.count.is_some() { let net_mask = network(&Ip { address: i.address.clone(), cidr: config diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ripcalc-0.3.1/src/main.rs new/ripcalc-0.4.0/src/main.rs --- old/ripcalc-0.3.1/src/main.rs 2026-05-21 21:26:33.000000000 +0200 +++ new/ripcalc-0.4.0/src/main.rs 2026-07-22 22:33:13.000000000 +0200 @@ -182,8 +182,8 @@ let nets = matches.opt_str("networks").unwrap().trim().parse().unwrap(); if nets < ip.cidr - && !(config_option_true(&config.borrow(), "encapsulating".to_string()) - && config_option_true(&config.borrow(), "group".to_string())) + && !(config_option_true(&config.borrow(), "encapsulating") + && config_option_true(&config.borrow(), "group")) { eprintln!("{} is bigger than the network mask {}", nets, ip.cidr); std::process::exit(1); @@ -393,7 +393,7 @@ fn process_json( u: &Value, - field_name: String, + field_name: &str, rows: &mut Option<HashMap<Ip, NetRow>>, input_base: Option<i32>, _reverse: &Reverse, @@ -440,7 +440,7 @@ fn process_csv( config: &RefCell<Config>, mut reader: csv::Reader<File>, - field_name: String, + field_name: &str, rows: &mut Option<HashMap<Ip, NetRow>>, input_base: Option<i32>, reverse: &Reverse, @@ -449,7 +449,7 @@ let mut header_names: Vec<String> = vec![]; let mut field_num: Option<usize> = None; for i in 0..headers.len() { - if headers[i] == field_name { + if headers[i] == *field_name { field_num = Some(i); } header_names.push(headers[i].to_string()); @@ -463,7 +463,7 @@ for result in reader.records() { if result.is_err() { - if !config_option_true(&config.borrow(), "quiet".to_string()) { + if !config_option_true(&config.borrow(), "quiet") { eprintln!("{:?}", result.err()); } continue; @@ -483,7 +483,7 @@ ); if ip.is_none() { - if !config_option_true(&config.borrow(), "quiet".to_string()) { + if !config_option_true(&config.borrow(), "quiet") { eprintln!("Could not parse {:?}", result.err()); } continue; @@ -524,7 +524,7 @@ increment_seen_count(&mut config.borrow_mut(), i.clone()); - if config_option_true(&config.borrow(), "group".to_string()) { + if config_option_true(&config.borrow(), "group") { match i.address { Addr::V4(_) => { if network_size > 32 { @@ -633,11 +633,11 @@ } fn filter_pos(config: &RefCell<Config>) -> Option<u32> { - if config_option_true(&config.borrow(), "filterany".to_string()) { + if config_option_true(&config.borrow(), "filterany") { return None; } - let position: u32 = if config_option_true(&config.borrow(), "filter".to_string()) { + let position: u32 = if config_option_true(&config.borrow(), "filter") { match config .borrow() .options @@ -760,7 +760,7 @@ std::process::exit(0); } - if config_option_true(&config.borrow(), "encapsulating".to_string()) { + if config_option_true(&config.borrow(), "encapsulating") { let mut used: HashMap<Ip, bool> = HashMap::new(); let mut network_size: u32 = 0; @@ -834,7 +834,7 @@ let _ = cdb.add("version".as_bytes(), "1".as_bytes()); } -fn make_cdb(path: String, config: &RefCell<Config>) { +fn make_cdb(path: &str, config: &RefCell<Config>) { let reader = BufReader::new(std::io::stdin()); let tmp_file = format!("{}.tmp", path); let cdb = cdb::CDBWriter::create(&tmp_file); @@ -885,15 +885,15 @@ std::process::exit(1); } - if fs::rename(&tmp_file, &path).is_err() { + if fs::rename(&tmp_file, path).is_err() { eprintln!("Cannot rename {} to {}", tmp_file, path); std::process::exit(1); } } -fn make_thyme_cdb(location: String, config: &RefCell<Config>) { +fn make_thyme_cdb(location: &str, config: &RefCell<Config>) { fn process_raw( - data: String, + data: &str, db: &mut HashMap<String, String>, asn_list: &mut HashMap<String, Vec<String>>, ) { @@ -919,7 +919,7 @@ } fn process_ipv6( - data: String, + data: &str, db: &mut HashMap<String, String>, asn_list: &mut HashMap<String, Vec<String>>, ) { @@ -946,7 +946,7 @@ fn process_asn( cdb: &mut cdb::CDBWriter, - lines: String, + lines: &str, asn: &mut HashMap<String, String>, data: &HashMap<String, String>, asn_list: &mut HashMap<String, Vec<String>>, @@ -1039,6 +1039,21 @@ } } + fn save_data(data_file: &str, source: &str, path: &str) -> bool { + use std::io::BufWriter; + + if !source.starts_with("http") { + return false; + } + + let file = File::create(path).unwrap_or_else(|_| panic!("Cannot open {}", path)); + let mut writer = BufWriter::new(file); + + let _ = writer.write(data_file.as_bytes()); + + true + } + let client = ua_client().expect("Cannot make user-agent"); let tmp_file = format!("{}.tmp", &location); let cdb = cdb::CDBWriter::create(&tmp_file); @@ -1057,19 +1072,37 @@ config.borrow().options.get("data-raw-table").unwrap(), &client, ); - process_raw(data_raw, &mut data, &mut asn_list); + let _ = config_option_true(&config.borrow(), "keep_data") + && save_data( + &data_raw, + config.borrow().options.get("data-raw-table").unwrap(), + "data-raw-table", + ); + process_raw(&data_raw, &mut data, &mut asn_list); let ipv6_raw = get_or_exit( config.borrow().options.get("ipv6-raw-table").unwrap(), &client, ); - process_ipv6(ipv6_raw, &mut data, &mut asn_list); + let _ = config_option_true(&config.borrow(), "keep_data") + && save_data( + &ipv6_raw, + config.borrow().options.get("ipv6-raw-table").unwrap(), + "ipv6-raw-table", + ); + process_ipv6(&ipv6_raw, &mut data, &mut asn_list); let data_used = get_or_exit( config.borrow().options.get("data-used-autnums").unwrap(), &client, ); - process_asn(&mut cdb, data_used, &mut asn, &data, &mut asn_list); + let _ = config_option_true(&config.borrow(), "keep_data") + && save_data( + &data_used, + config.borrow().options.get("data-used-autnums").unwrap(), + "data-used-autnums", + ); + process_asn(&mut cdb, &data_used, &mut asn, &data, &mut asn_list); generated_date(&mut cdb, Utc::now()); @@ -1078,7 +1111,7 @@ std::process::exit(1); } - if let Err(error) = fs::rename(&tmp_file, &location) { + if let Err(error) = fs::rename(&tmp_file, location) { eprintln!("Cannot rename {} to {}: {}", &tmp_file, &location, &error); std::process::exit(1); } @@ -1159,6 +1192,7 @@ "download and build a cdb file, defaults to thyme.apnic.net data", "PATH", ); + opts.optflag("", "keepdata", "keep data files after download"); opts.optopt("", "data-raw-table", "URL/path of raw data", "URL or PATH"); opts.optopt("", "ipv6-raw-table", "URL/path of ipv6 data", "URL or PATH"); opts.optopt( @@ -1176,7 +1210,7 @@ "top iterations, use --consume to produce whole report of stdin", "NUMBER", ); - opts.optflag("", "noclear", "don't print clear screen codes"); + opts.optopt("", "json", "json prefix file", "PATH"); opts.optopt( "", @@ -1201,7 +1235,8 @@ "CIDR", ); - opts.optopt("", "json", "json prefix file", "PATH"); + opts.optflag("", "noclear", "don't print clear screen codes"); + opts.optflag("q", "quiet", "don't report errors"); opts.optopt( @@ -1212,6 +1247,7 @@ ); opts.optopt("s", "file", "lookup addresses from, - for stdin", "PATH"); + opts.optflag("", "ula", "generate pseudorandom IPv6 ULA address"); opts.optflag("v", "version", "print version"); } @@ -1302,7 +1338,7 @@ }; let iterations = { - if config_option_true(&config.borrow(), "iterations".to_string()) { + if config_option_true(&config.borrow(), "iterations") { let cfg = config.borrow(); let iterations = cfg.options.get("iterations").unwrap(); @@ -1316,10 +1352,10 @@ } }; - if config_option_true(&config.borrow(), "consume".to_string()) { + if config_option_true(&config.borrow(), "consume") { loop { let config = count.lock().unwrap().clone(); - if !config_option_true(config.borrow(), "consumed".to_string()) { + if !config_option_true(config.borrow(), "consumed") { sleep(100); } else { break; @@ -1328,7 +1364,7 @@ } let mut loops = 0; - let clear_screen = !config_option_true(&config.borrow(), "noclear".to_string()); + let clear_screen = !config_option_true(&config.borrow(), "noclear"); let mut totals: Vec<usize> = vec![]; loop { if clear_screen { @@ -1623,7 +1659,14 @@ "network".to_string() }; - process_csv(&config, reader, field_name, &mut rows, input_base, &reverse); + process_csv( + &config, + reader, + &field_name, + &mut rows, + input_base, + &reverse, + ); } if matches.opt_present("json") { @@ -1639,7 +1682,7 @@ let u: Value = serde_json::from_reader(reader).unwrap_or_else(|_| panic!("Cannot parse {}", path)); - process_json(&u, field_name, &mut rows, input_base, &reverse, &config); + process_json(&u, &field_name, &mut rows, input_base, &reverse, &config); } } @@ -1688,7 +1731,7 @@ .borrow_mut() .options .insert("encapsulating".to_string(), "true".to_string()); - if !config_option_true(&config.borrow(), "group".to_string()) { + if !config_option_true(&config.borrow(), "group") { config .borrow_mut() .options @@ -1700,6 +1743,13 @@ input_mask = parse_mask(&v); } + if matches.opt_present("keepdata") { + config + .borrow_mut() + .options + .insert("keep_data".to_string(), "true".to_string()); + } + if let Some(v) = matches.opt_str("makethymecdb") { config.borrow_mut().options.insert( "data-raw-table".to_string(), @@ -1720,12 +1770,12 @@ .unwrap_or("https://thyme.apnic.net/.combined/data-used-autnums".to_string()), ); - make_thyme_cdb(v, &config); + make_thyme_cdb(&v, &config); std::process::exit(0); } if let Some(v) = matches.opt_str("makecdb") { - make_cdb(v, &config); + make_cdb(&v, &config); std::process::exit(0); } @@ -1875,6 +1925,10 @@ } } + if matches.opt_present("ula") { + ip_args = vec![ula_gen()]; + } + let mut used: HashMap<Ip, bool> = HashMap::new(); for arg in &ip_args { @@ -1893,7 +1947,7 @@ } } - if config_option_true(&config.borrow(), "encapsulating".to_string()) { + if config_option_true(&config.borrow(), "encapsulating") { let mut network_size: u32 = 0; if let Some(group) = config.borrow().options.get("group") { @@ -1918,7 +1972,7 @@ config.borrow_mut().used = Some(used.clone()); - if config_option_true(&config.borrow(), "encapsulating".to_string()) { + if config_option_true(&config.borrow(), "encapsulating") { process_group_encapsulation(&matches, used, &rows, &config); std::process::exit(0); } ++++++ ripcalc.obsinfo ++++++ --- /var/tmp/diff_new_pack.fh1SLu/_old 2026-08-18 16:37:51.312876852 +0200 +++ /var/tmp/diff_new_pack.fh1SLu/_new 2026-08-18 16:37:51.321877174 +0200 @@ -1,5 +1,5 @@ name: ripcalc -version: 0.3.1 -mtime: 1779391593 -commit: d206b49b3b1447102c9220fd3e2a66be8c68fcb9 +version: 0.4.0 +mtime: 1784752393 +commit: 279ea7fdc5c3b033f4ed4b9d3d210896516b45c8 ++++++ vendor.tar.zst ++++++ /work/SRC/openSUSE:Factory/ripcalc/vendor.tar.zst /work/SRC/openSUSE:Factory/.ripcalc.new.1258/vendor.tar.zst differ: char 7, line 1
