Skip to content

Commit 4db8df2

Browse files
committed
plugin: add rdns support
1 parent 7091dd9 commit 4db8df2

File tree

5 files changed

+210
-1
lines changed

5 files changed

+210
-1
lines changed

docs/plugin/executor/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ workflows:
2222
- [script](script)
2323
- [ecs](ecs)
2424
- [ipset](ipset)
25+
- [rdns](rdns)

docs/plugin/executor/rdns.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# rDNS IP 反查
2+
3+
rDNS 用于查询 IP 的反向域名,通常用于局域网内
4+
5+
```yaml
6+
plugin-executors:
7+
- tag: plugin
8+
type: rdns
9+
10+
workflows:
11+
- tag: default
12+
rules:
13+
- exec:
14+
- plugin:
15+
tag: plugin
16+
args: # 键值对:IP|CIDR: 上游服务器 Tag
17+
'*': upstream-A # '*' 用于匹配任意 IP
18+
'192.168.1.0/24': upstream-Local
19+
'fd00::/8': upstream-Local
20+
```

mkdocs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,5 @@ nav:
103103
- 'rediscache': plugin/executor/rediscache.md
104104
- 'script': plugin/executor/script.md
105105
- 'ecs': plugin/executor/ecs.md
106-
- 'ipset': plugin/executor/ipset.md
106+
- 'ipset': plugin/executor/ipset.md
107+
- 'rdns': plugin/executor/rdns.md

plugin/executor/init.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
_ "github.com/rnetx/cdns/plugin/executor/ecs"
55
_ "github.com/rnetx/cdns/plugin/executor/ipset"
66
_ "github.com/rnetx/cdns/plugin/executor/memcache"
7+
_ "github.com/rnetx/cdns/plugin/executor/rdns"
78
_ "github.com/rnetx/cdns/plugin/executor/rediscache"
89
_ "github.com/rnetx/cdns/plugin/executor/script"
910
)

plugin/executor/rdns/rdns.go

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package rdns
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/netip"
7+
"strings"
8+
9+
"github.com/rnetx/cdns/adapter"
10+
"github.com/rnetx/cdns/log"
11+
"github.com/rnetx/cdns/plugin"
12+
"github.com/rnetx/cdns/utils"
13+
14+
"github.com/miekg/dns"
15+
)
16+
17+
const Type = "rdns"
18+
19+
func init() {
20+
plugin.RegisterPluginExecutor(Type, NewRDNS)
21+
}
22+
23+
type rule struct {
24+
upstream adapter.Upstream
25+
rule netip.Prefix
26+
isAny bool
27+
}
28+
29+
var _ adapter.PluginExecutor = (*RDNS)(nil)
30+
31+
type RDNS struct {
32+
ctx context.Context
33+
core adapter.Core
34+
tag string
35+
logger log.Logger
36+
37+
runningArgsMap map[uint16][]rule
38+
}
39+
40+
func NewRDNS(ctx context.Context, core adapter.Core, logger log.Logger, tag string, _ any) (adapter.PluginExecutor, error) {
41+
r := &RDNS{
42+
ctx: ctx,
43+
core: core,
44+
tag: tag,
45+
logger: logger,
46+
}
47+
return r, nil
48+
}
49+
50+
func (r *RDNS) Tag() string {
51+
return r.tag
52+
}
53+
54+
func (r *RDNS) Type() string {
55+
return Type
56+
}
57+
58+
func (r *RDNS) Exec(ctx context.Context, dnsCtx *adapter.DNSContext, argsID uint16) (adapter.ReturnMode, error) {
59+
reqMsg := dnsCtx.ReqMsg()
60+
if reqMsg == nil {
61+
r.logger.DebugContext(ctx, "request message is nil")
62+
return adapter.ReturnModeContinue, nil
63+
}
64+
q := reqMsg.Question[0]
65+
ip := isIPv4rDNS(&q)
66+
if !ip.IsValid() {
67+
ip = isIPv6rDNS(&q)
68+
if !ip.IsValid() {
69+
r.logger.DebugContext(ctx, "not a reverse dns query")
70+
return adapter.ReturnModeContinue, nil
71+
}
72+
}
73+
rules := r.runningArgsMap[argsID]
74+
for _, rule := range rules {
75+
if rule.isAny || rule.rule.Contains(ip) {
76+
respMsg, err := rule.upstream.Exchange(ctx, reqMsg)
77+
if err != nil {
78+
return adapter.ReturnModeUnknown, err
79+
}
80+
dnsCtx.SetRespMsg(respMsg)
81+
return adapter.ReturnModeContinue, nil
82+
}
83+
}
84+
return adapter.ReturnModeContinue, nil
85+
}
86+
87+
func (r *RDNS) LoadRunningArgs(ctx context.Context, args any) (uint16, error) {
88+
var rawRuleMap map[string]string
89+
err := utils.JsonDecode(args, &rawRuleMap)
90+
if err != nil {
91+
return 0, err
92+
}
93+
if len(rawRuleMap) == 0 {
94+
return 0, fmt.Errorf("missing rule")
95+
}
96+
rules := make([]rule, 0, len(rawRuleMap))
97+
for ruleStr, upstreamTag := range rawRuleMap {
98+
isAny := ruleStr == "*"
99+
var prefix netip.Prefix
100+
if !isAny {
101+
prefix, err = netip.ParsePrefix(ruleStr)
102+
if err != nil {
103+
ip, err2 := netip.ParseAddr(ruleStr)
104+
if err2 != nil {
105+
return 0, fmt.Errorf("parse rule failed: %w | %w", err, err2)
106+
}
107+
bits := 0
108+
if ip.Is6() {
109+
bits = 128
110+
} else {
111+
bits = 32
112+
}
113+
prefix = netip.PrefixFrom(ip, bits)
114+
}
115+
}
116+
u := r.core.GetUpstream(upstreamTag)
117+
if u == nil {
118+
return 0, fmt.Errorf("upstream [%s] not found", upstreamTag)
119+
}
120+
rules = append(rules, rule{
121+
upstream: u,
122+
rule: prefix,
123+
isAny: isAny,
124+
})
125+
}
126+
if r.runningArgsMap == nil {
127+
r.runningArgsMap = make(map[uint16][]rule)
128+
}
129+
var id uint16
130+
for {
131+
id = utils.RandomIDUint16()
132+
if _, ok := r.runningArgsMap[id]; !ok {
133+
break
134+
}
135+
}
136+
r.runningArgsMap[id] = rules
137+
return id, nil
138+
}
139+
140+
func isIPv4rDNS(q *dns.Question) netip.Addr {
141+
if q.Qtype == dns.TypePTR && q.Qclass == dns.ClassINET && strings.HasSuffix(q.Name, ".in-addr.arpa.") {
142+
name := q.Name[:len(q.Name)-len(".in-addr.arpa.")]
143+
ips := strings.Split(name, ".")
144+
if len(ips) != 4 {
145+
return netip.Addr{}
146+
}
147+
ipStr := fmt.Sprintf("%s.%s.%s.%s", ips[3], ips[2], ips[1], ips[0])
148+
ip, err := netip.ParseAddr(ipStr)
149+
if err == nil {
150+
if ip.Is4() {
151+
return ip
152+
}
153+
}
154+
}
155+
return netip.Addr{}
156+
}
157+
158+
func isIPv6rDNS(q *dns.Question) netip.Addr {
159+
if q.Qtype == dns.TypePTR && q.Qclass == dns.ClassINET && strings.HasSuffix(q.Name, ".ip6.arpa.") {
160+
name := q.Name[:len(q.Name)-len(".ip6.arpa.")]
161+
rawIPStr := ""
162+
s := 0
163+
m := 0
164+
for i := len(name) - 1; i >= 0; i-- {
165+
if s == 4 {
166+
rawIPStr += ":"
167+
m++
168+
s = 0
169+
}
170+
if name[i] != '.' {
171+
rawIPStr += string(name[i])
172+
s++
173+
}
174+
}
175+
if m != 7 {
176+
rawIPStr += "::"
177+
}
178+
ip, err := netip.ParseAddr(rawIPStr)
179+
if err == nil {
180+
if ip.Is6() {
181+
return ip
182+
}
183+
}
184+
}
185+
return netip.Addr{}
186+
}

0 commit comments

Comments
 (0)