Security

IP ranges

IP addresses against ranges (CIDR), IPv4 and IPv6: allowlists, office-only pages, private addresses.

Version 1.0.0 · 3 functions · 1 crate

#ip#allowlists#admin#networks

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 error

Whether address is in any of ranges ("10.0.0.0/8", "2001:db8::/32", or a single address).

ip_private(address: str) → boolor an error

Whether address is private, loopback or link-local: not one from the internet.

ip_version(address: str) → intor an error

4 or 6.

Get it

$ quillo install ipnet               # ipnet.qwer into this folder$ quillo install ipnet --example     # and its example, ipnet.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/ipnet.qwer$ curl -O https://quillolang.org/plugins/files/ipnet.qwe

Then, 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 these

It 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
87Useful? Like it, or add to its thread below.

Thread 0 posts

On IP ranges: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.