IP ranges
IP addresses against ranges (CIDR), IPv4 and IPv6: allowlists, office-only pages, private addresses.
Version 1.0.0 · 3 functions · 1 crate
Functions
What use ipnet.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use ip_in, ip_private from ipnet (Quillo 0.6).
ip_in(address: str, ranges: list) → boolor an errorWhether address is in any of ranges ("10.0.0.0/8", "2001:db8::/32", or a single address).
ip_private(address: str) → boolor an errorWhether address is private, loopback or link-local: not one from the internet.
ip_version(address: str) → intor an error4 or 6.
Get it
$ quillo install ipnet # ipnet.qwer into this folder$ quillo install ipnet --example # and its example, ipnet.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/ipnet.qwer$ curl -O https://quillolang.org/plugins/files/ipnet.qweThen, in your program, all of it or only what you call:
main.qwe
use ipnet.qweruse ip_in, ip_private from ipnet // Quillo 0.6: only theseIt uses this crate, which cargo fetches the first time the plugin is built:
[dependencies]
ipnet = "2"Example
ipnet.qwe
// An admin page open only from the office and the VPN.//// quillo run ipnet.qwe then open http://localhost:8080/adminuse "ipnet.qwer"let OFFICE = ["127.0.0.1", "::1", "10.20.0.0/16", "2001:db8:42::/48"]fn office_only(req) { if !ip_in(req.ip, OFFICE)? { reply 403 "From the office only" } req}route GET /admin = request | office_only | ((r) => { welcome: "admin", from: r.ip })route GET /samples = { vpn: ip_in("10.20.3.4", OFFICE)?, outside: ip_in("8.8.8.8", OFFICE)?, private: ip_private("192.168.1.9")?, v6: ip_version("2001:db8::1")? }$ quillo run ipnet.qwe$ curl 'localhost:8080/admin'$ curl 'localhost:8080/samples'Source
ipnet.qwer
//! IP addresses and ranges (CIDR): allowlists, office-only admin pages, private addresses, IPv4 and IPv6 alike.//! version: 1.0.0//!//! [dependencies]//! ipnet = "2"use ipnet::IpNet;use std::net::IpAddr;fn ip(s: &str) -> Result<IpAddr, String> { s.trim().parse::<IpAddr>().map_err(|_| format!("`{}` is not an IP address", s))}/// Whether `address` is in any of `ranges` ("10.0.0.0/8", "2001:db8::/32", or a single address).pub fn ip_in(address: &str, ranges: Vec<String>) -> Result<bool, String> { let a = ip(address)?; for r in &ranges { let r = r.trim(); let hit = if r.contains('/') { r.parse::<IpNet>().map_err(|e| format!("`{}`: {}", r, e))?.contains(&a) } else { ip(r)? == a }; if hit { return Ok(true); } } Ok(false)}/// Whether `address` is private, loopback or link-local: not one from the internet.pub fn ip_private(address: &str) -> Result<bool, String> { Ok(match ip(address)? { IpAddr::V4(v) => v.is_private() || v.is_loopback() || v.is_link_local(), IpAddr::V6(v) => v.is_loopback() || (v.segments()[0] & 0xfe00) == 0xfc00 || (v.segments()[0] & 0xffc0) == 0xfe80 || v.to_ipv4_mapped().is_some_and(|m| m.is_private() || m.is_loopback()), })}/// 4 or 6.pub fn ip_version(address: &str) -> Result<i64, String> { Ok(if ip(address)?.is_ipv4() { 4 } else { 6 })}Thread
Thread 0 posts
On IP ranges: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.