mirror of
https://github.com/therootcompany/golib.git
synced 2026-11-04 15:05:00 +00:00
IPv4 host + CIDR membership testing backed by sorted /32 binary search and a linear scan of CIDR ranges. CSV/text loader with bounded reads (maxBytes is required so callers pick a limit appropriate to their input source — 1 KB allowlists through 100+ MB threat feeds). Returns errors for unparseable IPs so callers can decide fail-open vs fail-closed; partial cohorts are returned alongside parse errors.
37 lines
1.2 KiB
Markdown
37 lines
1.2 KiB
Markdown
# [ipcohort](https://github.com/therootcompany/golib/tree/main/net/ipcohort)
|
|
|
|
A memory-efficient, fast IP cohort checker for blacklists, whitelists, and ad cohorts.
|
|
|
|
- 4 bytes per /32 host, 5 bytes per CIDR range (8 with alignment padding)
|
|
- O(log n) binary search across both hosts and CIDR ranges
|
|
- immutable cohorts — callers swap via `atomic.Pointer` for lock-free reads
|
|
- requires CIDR sources to be an anti-chain (no entry contained in another;
|
|
a /8 supersedes any /24 inside it). Most curated blocklists already
|
|
ship pre-normalized this way.
|
|
|
|
## Example
|
|
|
|
Check if an IP address belongs to a cohort (such as a blacklist):
|
|
|
|
```go
|
|
cohort, err := ipcohort.LoadFile("/srv/data/inbound.txt")
|
|
if err != nil {
|
|
log.Fatalf("load: %v", err)
|
|
}
|
|
|
|
blocked, err := cohort.Contains("92.255.85.72")
|
|
if err != nil {
|
|
log.Fatalf("parse: %v", err)
|
|
}
|
|
if blocked {
|
|
fmt.Println("BLOCKED")
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("allowed")
|
|
```
|
|
|
|
`Cohort.Contains(string)` parses the address each call and returns an error
|
|
on unparseable input — callers decide fail-open vs fail-closed. If you
|
|
already have a `netip.Addr` (e.g. from `netip.ParseAddr` on a request peer),
|
|
use `Cohort.ContainsAddr(netip.Addr)` to skip the parse and the error.
|