Skip to content

Commit b618b0d

Browse files
committed
Add support for CIDR ranges in the IP allow/block plugins
1 parent 1661a56 commit b618b0d

File tree

5 files changed

+66
-14
lines changed

5 files changed

+66
-14
lines changed

dnscrypt-proxy/common.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
iradix "github.com/hashicorp/go-immutable-radix"
1818
"github.com/jedisct1/dlog"
19+
"github.com/k-sone/critbitgo"
1920
)
2021

2122
type CryptoConstruction uint16
@@ -313,9 +314,22 @@ func ProcessConfigLines(lines string, processor func(line string, lineNo int) er
313314
return nil
314315
}
315316

316-
// LoadIPRules loads IP rules from text lines into radix tree and map structures
317-
func LoadIPRules(lines string, prefixes *iradix.Tree, ips map[string]any) (*iradix.Tree, error) {
317+
// LoadIPRules loads IP rules from text lines into three structures:
318+
// - ips (map): exact IP addresses
319+
// - prefixes (radix tree): wildcard prefix rules (e.g. "192.168.*")
320+
// - networks (critbit net): CIDR network masks (e.g. "10.0.0.0/8")
321+
func LoadIPRules(lines string, prefixes *iradix.Tree, ips map[string]any, networks *critbitgo.Net) (*iradix.Tree, error) {
318322
err := ProcessConfigLines(lines, func(line string, lineNo int) error {
323+
if strings.Contains(line, "/") {
324+
if networks == nil {
325+
dlog.Errorf("CIDR rule [%s] at line %d but no network table provided", line, lineNo)
326+
return nil
327+
}
328+
if err := networks.AddCIDR(line, true); err != nil {
329+
dlog.Errorf("Invalid CIDR rule [%s] at line %d: %v", line, lineNo, err)
330+
}
331+
return nil
332+
}
319333
cleanLine, trailingStar, lineErr := ParseIPRule(line, lineNo)
320334
if lineErr != nil {
321335
dlog.Error(lineErr)

dnscrypt-proxy/example-allowed-ips.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
#192.168.0.*
66
#fe80:53:* # IPv6 prefix example
77
#81.169.145.105
8+
#192.168.0.0/16 # CIDR network mask
9+
#fe80::/10 # IPv6 CIDR network mask

dnscrypt-proxy/example-blocked-ips.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@
1515
163.5.1.4
1616
94.46.118.*
1717
fe80:53:* # IPv6 prefix example
18+
10.0.0.0/8 # CIDR network mask
19+
fe80::/10 # IPv6 CIDR network mask

dnscrypt-proxy/plugin_allow_ip.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ package main
33
import (
44
"errors"
55
"io"
6+
"net"
67
"sync"
78

89
"codeberg.org/miekg/dns"
910
iradix "github.com/hashicorp/go-immutable-radix"
1011
"github.com/jedisct1/dlog"
12+
"github.com/k-sone/critbitgo"
1113
)
1214

1315
type PluginAllowedIP struct {
1416
allowedPrefixes *iradix.Tree
1517
allowedIPs map[string]any
18+
allowedNetworks *critbitgo.Net
1619
logger io.Writer
1720
format string
1821
ipCryptConfig *IPCryptConfig
@@ -23,6 +26,7 @@ type PluginAllowedIP struct {
2326
configWatcher *ConfigWatcher
2427
stagingPrefixes *iradix.Tree
2528
stagingIPs map[string]any
29+
stagingNetworks *critbitgo.Net
2630
}
2731

2832
func (plugin *PluginAllowedIP) Name() string {
@@ -44,8 +48,9 @@ func (plugin *PluginAllowedIP) Init(proxy *Proxy) error {
4448

4549
plugin.allowedPrefixes = iradix.New()
4650
plugin.allowedIPs = make(map[string]any)
51+
plugin.allowedNetworks = critbitgo.NewNet()
4752

48-
plugin.allowedPrefixes, err = plugin.loadRules(lines, plugin.allowedPrefixes, plugin.allowedIPs)
53+
plugin.allowedPrefixes, err = plugin.loadRules(lines, plugin.allowedPrefixes, plugin.allowedIPs, plugin.allowedNetworks)
4954
if err != nil {
5055
return err
5156
}
@@ -56,9 +61,9 @@ func (plugin *PluginAllowedIP) Init(proxy *Proxy) error {
5661
return nil
5762
}
5863

59-
// loadRules parses and loads IP rules into the provided tree and map
60-
func (plugin *PluginAllowedIP) loadRules(lines string, prefixes *iradix.Tree, ips map[string]any) (*iradix.Tree, error) {
61-
return LoadIPRules(lines, prefixes, ips)
64+
// loadRules parses and loads IP rules into the provided tree, map, and network table
65+
func (plugin *PluginAllowedIP) loadRules(lines string, prefixes *iradix.Tree, ips map[string]any, networks *critbitgo.Net) (*iradix.Tree, error) {
66+
return LoadIPRules(lines, prefixes, ips, networks)
6267
}
6368

6469
func (plugin *PluginAllowedIP) Drop() error {
@@ -74,27 +79,30 @@ func (plugin *PluginAllowedIP) PrepareReload() error {
7479
// Create staging structures
7580
plugin.stagingPrefixes = iradix.New()
7681
plugin.stagingIPs = make(map[string]any)
82+
plugin.stagingNetworks = critbitgo.NewNet()
7783

7884
// Load rules into staging structures
7985
var err error
80-
plugin.stagingPrefixes, err = plugin.loadRules(lines, plugin.stagingPrefixes, plugin.stagingIPs)
86+
plugin.stagingPrefixes, err = plugin.loadRules(lines, plugin.stagingPrefixes, plugin.stagingIPs, plugin.stagingNetworks)
8187
return err
8288
})
8389
}
8490

8591
// ApplyReload atomically replaces the active rules with the staging ones
8692
func (plugin *PluginAllowedIP) ApplyReload() error {
8793
return StandardApplyReloadPattern(plugin.Name(), func() error {
88-
if plugin.stagingPrefixes == nil || plugin.stagingIPs == nil {
94+
if plugin.stagingPrefixes == nil || plugin.stagingIPs == nil || plugin.stagingNetworks == nil {
8995
return errors.New("no staged configuration to apply")
9096
}
9197

9298
// Use write lock to swap rule structures
9399
plugin.rwLock.Lock()
94100
plugin.allowedPrefixes = plugin.stagingPrefixes
95101
plugin.allowedIPs = plugin.stagingIPs
102+
plugin.allowedNetworks = plugin.stagingNetworks
96103
plugin.stagingPrefixes = nil
97104
plugin.stagingIPs = nil
105+
plugin.stagingNetworks = nil
98106
plugin.rwLock.Unlock()
99107

100108
return nil
@@ -105,6 +113,7 @@ func (plugin *PluginAllowedIP) ApplyReload() error {
105113
func (plugin *PluginAllowedIP) CancelReload() {
106114
plugin.stagingPrefixes = nil
107115
plugin.stagingIPs = nil
116+
plugin.stagingNetworks = nil
108117
}
109118

110119
// Reload implements hot-reloading for the plugin
@@ -165,6 +174,14 @@ func (plugin *PluginAllowedIP) Eval(pluginsState *PluginsState, msg *dns.Msg) er
165174
break
166175
}
167176
}
177+
if plugin.allowedNetworks.Size() > 0 {
178+
if ip := net.ParseIP(ipStr); ip != nil {
179+
if route, _, _ := plugin.allowedNetworks.MatchIP(ip); route != nil {
180+
allowed, reason = true, route.String()
181+
break
182+
}
183+
}
184+
}
168185
}
169186

170187
if allowed {

dnscrypt-proxy/plugin_block_ip.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ package main
33
import (
44
"errors"
55
"io"
6+
"net"
67
"sync"
78

89
"codeberg.org/miekg/dns"
910
iradix "github.com/hashicorp/go-immutable-radix"
1011
"github.com/jedisct1/dlog"
12+
"github.com/k-sone/critbitgo"
1113
)
1214

1315
type PluginBlockIP struct {
1416
blockedPrefixes *iradix.Tree
1517
blockedIPs map[string]any
18+
blockedNetworks *critbitgo.Net
1619
logger io.Writer
1720
format string
1821
ipCryptConfig *IPCryptConfig
@@ -23,6 +26,7 @@ type PluginBlockIP struct {
2326
configWatcher *ConfigWatcher
2427
stagingPrefixes *iradix.Tree
2528
stagingIPs map[string]any
29+
stagingNetworks *critbitgo.Net
2630
}
2731

2832
func (plugin *PluginBlockIP) Name() string {
@@ -44,8 +48,9 @@ func (plugin *PluginBlockIP) Init(proxy *Proxy) error {
4448

4549
plugin.blockedPrefixes = iradix.New()
4650
plugin.blockedIPs = make(map[string]any)
51+
plugin.blockedNetworks = critbitgo.NewNet()
4752

48-
plugin.blockedPrefixes, err = plugin.loadRules(lines, plugin.blockedPrefixes, plugin.blockedIPs)
53+
plugin.blockedPrefixes, err = plugin.loadRules(lines, plugin.blockedPrefixes, plugin.blockedIPs, plugin.blockedNetworks)
4954
if err != nil {
5055
return err
5156
}
@@ -56,9 +61,9 @@ func (plugin *PluginBlockIP) Init(proxy *Proxy) error {
5661
return nil
5762
}
5863

59-
// loadRules parses and loads IP rules into the provided tree and map
60-
func (plugin *PluginBlockIP) loadRules(lines string, prefixes *iradix.Tree, ips map[string]any) (*iradix.Tree, error) {
61-
return LoadIPRules(lines, prefixes, ips)
64+
// loadRules parses and loads IP rules into the provided tree, map, and network table
65+
func (plugin *PluginBlockIP) loadRules(lines string, prefixes *iradix.Tree, ips map[string]any, networks *critbitgo.Net) (*iradix.Tree, error) {
66+
return LoadIPRules(lines, prefixes, ips, networks)
6267
}
6368

6469
func (plugin *PluginBlockIP) Drop() error {
@@ -74,27 +79,30 @@ func (plugin *PluginBlockIP) PrepareReload() error {
7479
// Create staging structures
7580
plugin.stagingPrefixes = iradix.New()
7681
plugin.stagingIPs = make(map[string]any)
82+
plugin.stagingNetworks = critbitgo.NewNet()
7783

7884
// Load rules into staging structures
7985
var err error
80-
plugin.stagingPrefixes, err = plugin.loadRules(lines, plugin.stagingPrefixes, plugin.stagingIPs)
86+
plugin.stagingPrefixes, err = plugin.loadRules(lines, plugin.stagingPrefixes, plugin.stagingIPs, plugin.stagingNetworks)
8187
return err
8288
})
8389
}
8490

8591
// ApplyReload atomically replaces the active rules with the staging ones
8692
func (plugin *PluginBlockIP) ApplyReload() error {
8793
return StandardApplyReloadPattern(plugin.Name(), func() error {
88-
if plugin.stagingPrefixes == nil || plugin.stagingIPs == nil {
94+
if plugin.stagingPrefixes == nil || plugin.stagingIPs == nil || plugin.stagingNetworks == nil {
8995
return errors.New("no staged configuration to apply")
9096
}
9197

9298
// Use write lock to swap rule structures
9399
plugin.rwLock.Lock()
94100
plugin.blockedPrefixes = plugin.stagingPrefixes
95101
plugin.blockedIPs = plugin.stagingIPs
102+
plugin.blockedNetworks = plugin.stagingNetworks
96103
plugin.stagingPrefixes = nil
97104
plugin.stagingIPs = nil
105+
plugin.stagingNetworks = nil
98106
plugin.rwLock.Unlock()
99107

100108
return nil
@@ -105,6 +113,7 @@ func (plugin *PluginBlockIP) ApplyReload() error {
105113
func (plugin *PluginBlockIP) CancelReload() {
106114
plugin.stagingPrefixes = nil
107115
plugin.stagingIPs = nil
116+
plugin.stagingNetworks = nil
108117
}
109118

110119
// Reload implements hot-reloading for the plugin
@@ -169,6 +178,14 @@ func (plugin *PluginBlockIP) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
169178
break
170179
}
171180
}
181+
if plugin.blockedNetworks.Size() > 0 {
182+
if ip := net.ParseIP(ipStr); ip != nil {
183+
if route, _, _ := plugin.blockedNetworks.MatchIP(ip); route != nil {
184+
reject, reason = true, route.String()
185+
break
186+
}
187+
}
188+
}
172189
}
173190

174191
if reject {

0 commit comments

Comments
 (0)