mirror of
https://github.com/apernet/OpenGFW.git
synced 2024-12-23 09:29:22 +08:00
feat: add multiple addresses support for DNS modifier
This commit is contained in:
parent
1de95ed53e
commit
f5741f61ac
@ -4,7 +4,7 @@ type Modifier interface {
|
|||||||
// Name returns the name of the modifier.
|
// Name returns the name of the modifier.
|
||||||
Name() string
|
Name() string
|
||||||
// New returns a new modifier instance.
|
// New returns a new modifier instance.
|
||||||
New(args map[string]interface{}) (Instance, error)
|
New(args map[string][]interface{}) (Instance, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Instance interface{}
|
type Instance interface{}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package udp
|
package udp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"hash/fnv"
|
||||||
|
"math/rand/v2"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/apernet/OpenGFW/modifier"
|
"github.com/apernet/OpenGFW/modifier"
|
||||||
@ -24,23 +27,28 @@ func (m *DNSModifier) Name() string {
|
|||||||
return "dns"
|
return "dns"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DNSModifier) New(args map[string]interface{}) (modifier.Instance, error) {
|
func (m *DNSModifier) New(args map[string][]interface{}) (modifier.Instance, error) {
|
||||||
i := &dnsModifierInstance{}
|
i := &dnsModifierInstance{}
|
||||||
aStr, ok := args["a"].(string)
|
i.seed = rand.Uint32()
|
||||||
|
for _, arg := range args["a"] {
|
||||||
|
aStr, ok := arg.(string)
|
||||||
if ok {
|
if ok {
|
||||||
a := net.ParseIP(aStr).To4()
|
a := net.ParseIP(aStr).To4()
|
||||||
if a == nil {
|
if a == nil {
|
||||||
return nil, &modifier.ErrInvalidArgs{Err: errInvalidIP}
|
return nil, &modifier.ErrInvalidArgs{Err: errInvalidIP}
|
||||||
}
|
}
|
||||||
i.A = a
|
i.A = append(i.A, a)
|
||||||
}
|
}
|
||||||
aaaaStr, ok := args["aaaa"].(string)
|
}
|
||||||
|
for _, arg := range args["aaaa"] {
|
||||||
|
aaaaStr, ok := arg.(string)
|
||||||
if ok {
|
if ok {
|
||||||
aaaa := net.ParseIP(aaaaStr).To16()
|
aaaa := net.ParseIP(aaaaStr).To16()
|
||||||
if aaaa == nil {
|
if aaaa == nil {
|
||||||
return nil, &modifier.ErrInvalidArgs{Err: errInvalidIP}
|
return nil, &modifier.ErrInvalidArgs{Err: errInvalidIP}
|
||||||
}
|
}
|
||||||
i.AAAA = aaaa
|
i.AAAA = append(i.AAAA, aaaa)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
@ -48,8 +56,9 @@ func (m *DNSModifier) New(args map[string]interface{}) (modifier.Instance, error
|
|||||||
var _ modifier.UDPModifierInstance = (*dnsModifierInstance)(nil)
|
var _ modifier.UDPModifierInstance = (*dnsModifierInstance)(nil)
|
||||||
|
|
||||||
type dnsModifierInstance struct {
|
type dnsModifierInstance struct {
|
||||||
A net.IP
|
A []net.IP
|
||||||
AAAA net.IP
|
AAAA []net.IP
|
||||||
|
seed uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *dnsModifierInstance) Process(data []byte) ([]byte, error) {
|
func (i *dnsModifierInstance) Process(data []byte) ([]byte, error) {
|
||||||
@ -64,26 +73,41 @@ func (i *dnsModifierInstance) Process(data []byte) ([]byte, error) {
|
|||||||
if len(dns.Questions) == 0 {
|
if len(dns.Questions) == 0 {
|
||||||
return nil, &modifier.ErrInvalidPacket{Err: errEmptyDNSQuestion}
|
return nil, &modifier.ErrInvalidPacket{Err: errEmptyDNSQuestion}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash the query name so that DNS response is fixed for a given query.
|
||||||
|
// Use a random seed to avoid determinism.
|
||||||
|
hashStringToIndex := func(b []byte, sliceLength int, seed uint32) int {
|
||||||
|
h := fnv.New32a()
|
||||||
|
seedBytes := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(seedBytes, seed)
|
||||||
|
h.Write(seedBytes)
|
||||||
|
h.Write(b)
|
||||||
|
hashValue := h.Sum32()
|
||||||
|
return int(hashValue % uint32(sliceLength))
|
||||||
|
}
|
||||||
|
|
||||||
// In practice, most if not all DNS clients only send one question
|
// In practice, most if not all DNS clients only send one question
|
||||||
// per packet, so we don't care about the rest for now.
|
// per packet, so we don't care about the rest for now.
|
||||||
q := dns.Questions[0]
|
q := dns.Questions[0]
|
||||||
switch q.Type {
|
switch q.Type {
|
||||||
case layers.DNSTypeA:
|
case layers.DNSTypeA:
|
||||||
if i.A != nil {
|
if i.A != nil {
|
||||||
|
idx := hashStringToIndex(q.Name, len(i.A), i.seed)
|
||||||
dns.Answers = []layers.DNSResourceRecord{{
|
dns.Answers = []layers.DNSResourceRecord{{
|
||||||
Name: q.Name,
|
Name: q.Name,
|
||||||
Type: layers.DNSTypeA,
|
Type: layers.DNSTypeA,
|
||||||
Class: layers.DNSClassIN,
|
Class: layers.DNSClassIN,
|
||||||
IP: i.A,
|
IP: i.A[idx],
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
case layers.DNSTypeAAAA:
|
case layers.DNSTypeAAAA:
|
||||||
if i.AAAA != nil {
|
if i.AAAA != nil {
|
||||||
|
idx := hashStringToIndex(q.Name, len(i.AAAA), i.seed)
|
||||||
dns.Answers = []layers.DNSResourceRecord{{
|
dns.Answers = []layers.DNSResourceRecord{{
|
||||||
Name: q.Name,
|
Name: q.Name,
|
||||||
Type: layers.DNSTypeAAAA,
|
Type: layers.DNSTypeAAAA,
|
||||||
Class: layers.DNSClassIN,
|
Class: layers.DNSClassIN,
|
||||||
IP: i.AAAA,
|
IP: i.AAAA[idx],
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ type ExprRule struct {
|
|||||||
|
|
||||||
type ModifierEntry struct {
|
type ModifierEntry struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Args map[string]interface{} `yaml:"args"`
|
Args map[string][]interface{} `yaml:"args"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExprRulesFromYAML(file string) ([]ExprRule, error) {
|
func ExprRulesFromYAML(file string) ([]ExprRule, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user