Repository navigation
v1.12.14.21
Pre-releasesing-box v1.12.14.21
π CRITICAL FIX: Extract IP Rules from IPSet Field
This release fixes a critical bug where geoip .srs files were NOT being parsed correctly, resulting in 0 IP rules being extracted.
The Bug
When loading .srs geoip files (like geoip-cn.srs from SagerNet/sing-geoip), the IP extraction was completely broken:
Root cause:
- geoip .srs files store IPs in
IPSetfield (*netipx.IPSetobject) - But extraction code only looked at
IPCIDRarray (which is empty in geoip files) - Result: 0 IPs extracted from geoip files
Evidence:
// Before fix - only checked IPCIDR array:
result.IPCIDRs = append(result.IPCIDRs, ruleOpt.DefaultOptions.IPCIDR...)
// For geoip-cn.srs: IPCIDR = [] (empty!) β 0 IPs extractedWhat actually happened:
Testing geoip-cn.srs:
IPCIDR array: [] (empty)
IPSet field: *netipx.IPSet with 7540 prefixes
Extracted before fix: 0 IPs β
Extracted after fix: 7540 IPs β
The Fix
Now checks BOTH IPCIDR array AND IPSet object:
// Extract from IPCIDR string array (for JSON rulesets)
result.IPCIDRs = append(result.IPCIDRs, ruleOpt.DefaultOptions.IPCIDR...)
// Extract from IPSet object (for .srs geoip files) β NEW!
if ruleOpt.DefaultOptions.IPSet != nil {
for _, prefix := range ruleOpt.DefaultOptions.IPSet.Prefixes() {
result.IPCIDRs = append(result.IPCIDRs, prefix.String())
}
}Impact
Before this fix:
- β geoip .srs files: 0 IPs extracted
- β Hash-based IP matching: completely broken
- β IP interval tree: empty (0 IPv4, 0 IPv6)
- β Only domain regex matching worked
After this fix:
- β geoip .srs files: correctly extracts all IP CIDRs
- β Hash-based IP matching: fully functional
- β IP interval tree: populated with thousands of ranges
- β Both domain AND IP matching work
Example
geoip-cn.srs before:
INFO router: built IP match index: 0 IPv4, 0 IPv6 β Empty!
geoip-cn.srs after:
INFO router: built IP match index: 7540 IPv4, 0 IPv6 β Populated!
INFO hash ruleset matched (ip): ip=1.0.1.1 β ruleset=geoip-cn
Who Should Upgrade
If you're using geoip-cn.srs, geoip-us.srs, etc. for hash-based routing, the IP matching has been completely non-functional until now.
This also affects regular routing rulesets using geoip .srs files.
Verification
After upgrading with debug logging enabled:
# You should now see:
DEBUG extracted from geoip-cn: 0 exact, 0 suffixes, 0 keywords, 0 regex
DEBUG extracted IP rules: 7540 CIDRs β NEW!
INFO router: built IP match index: 7540 IPv4, 0 IPv6 β Populated!
# And when connections match:
INFO hash ruleset matched (ip): ip=1.0.1.1 β ruleset=geoip-cnFiles Changed
- route/rule/rule_set_local.go:
- Modified
ExtractIPRules()to checkIPSetfield - Convert IPSet prefixes to CIDR strings
- Works for both routing and hash-only rulesets
- Modified
Technical Details
.srs file structure:
Geosite files (domain rules):
{
"domain_regex": ["^.*\\.google\\.com$"]
}Geoip files (IP rules):
{
// IPCIDR array is EMPTY!
// IP data is in IPSet object instead
}The IPSet field is a *netipx.IPSet which efficiently stores IP ranges as prefixes. We extract them using:
for _, prefix := range ruleOpt.DefaultOptions.IPSet.Prefixes() {
result.IPCIDRs = append(result.IPCIDRs, prefix.String())
}Performance Impact
Minimal:
- IPSet.Prefixes() returns already-parsed prefix objects
- String conversion is O(1) per prefix
- One-time cost at startup
- No runtime performance impact
Build Information
- Version: 1.12.14.21
- Build Date: 2026-01-30
- Build Tags:
with_acme,with_clash_api,with_dhcp,with_embedded_tor,with_grpc,with_gvisor,with_low_memory,with_quic,with_shadowsocksr,with_utls,with_wireguard,with_tailscale
Download
All binaries built with full feature support.
Platforms
- Linux: amd64, arm64
- Windows: amd64, arm64
- macOS: amd64 (Intel), arm64 (Apple Silicon)
Full Changelog: v1.12.14.20...v1.12.14.21