mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-01-30 20:56:54 +00:00
155 lines
3.7 KiB
Go
155 lines
3.7 KiB
Go
package option
|
|
|
|
import (
|
|
"encoding/base64"
|
|
|
|
"github.com/sagernet/sing/common"
|
|
"github.com/sagernet/sing/common/buf"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
"github.com/sagernet/sing/common/json"
|
|
"github.com/sagernet/sing/common/json/badoption"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
type PredefinedDNSServerOptions struct {
|
|
Responses []DNSResponseOptions `json:"responses,omitempty"`
|
|
}
|
|
|
|
type DNSResponseOptions struct {
|
|
Query badoption.Listable[string] `json:"query,omitempty"`
|
|
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
|
|
|
RCode *DNSRCode `json:"rcode,omitempty"`
|
|
Answer badoption.Listable[DNSRecordOptions] `json:"answer,omitempty"`
|
|
Ns badoption.Listable[DNSRecordOptions] `json:"ns,omitempty"`
|
|
Extra badoption.Listable[DNSRecordOptions] `json:"extra,omitempty"`
|
|
}
|
|
|
|
type DNSRCode int
|
|
|
|
func (r DNSRCode) MarshalJSON() ([]byte, error) {
|
|
rCodeValue, loaded := dns.RcodeToString[int(r)]
|
|
if loaded {
|
|
return json.Marshal(rCodeValue)
|
|
}
|
|
return json.Marshal(int(r))
|
|
}
|
|
|
|
func (r *DNSRCode) UnmarshalJSON(bytes []byte) error {
|
|
var intValue int
|
|
err := json.Unmarshal(bytes, &intValue)
|
|
if err == nil {
|
|
*r = DNSRCode(intValue)
|
|
return nil
|
|
}
|
|
var stringValue string
|
|
err = json.Unmarshal(bytes, &stringValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rCodeValue, loaded := dns.StringToRcode[stringValue]
|
|
if !loaded {
|
|
return E.New("unknown rcode: " + stringValue)
|
|
}
|
|
*r = DNSRCode(rCodeValue)
|
|
return nil
|
|
}
|
|
|
|
func (r *DNSRCode) Build() int {
|
|
if r == nil {
|
|
return dns.RcodeSuccess
|
|
}
|
|
return int(*r)
|
|
}
|
|
|
|
func (o DNSResponseOptions) Build() ([]dns.Question, *dns.Msg, error) {
|
|
var questions []dns.Question
|
|
if len(o.Query) == 0 && len(o.QueryType) == 0 {
|
|
questions = []dns.Question{{Qclass: dns.ClassINET}}
|
|
} else if len(o.Query) == 0 {
|
|
for _, queryType := range o.QueryType {
|
|
questions = append(questions, dns.Question{
|
|
Qtype: uint16(queryType),
|
|
Qclass: dns.ClassINET,
|
|
})
|
|
}
|
|
} else if len(o.QueryType) == 0 {
|
|
for _, domain := range o.Query {
|
|
questions = append(questions, dns.Question{
|
|
Name: dns.Fqdn(domain),
|
|
Qclass: dns.ClassINET,
|
|
})
|
|
}
|
|
} else {
|
|
for _, queryType := range o.QueryType {
|
|
for _, domain := range o.Query {
|
|
questions = append(questions, dns.Question{
|
|
Name: dns.Fqdn(domain),
|
|
Qtype: uint16(queryType),
|
|
Qclass: dns.ClassINET,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return questions, &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Response: true,
|
|
Rcode: o.RCode.Build(),
|
|
},
|
|
Answer: common.Map(o.Answer, DNSRecordOptions.build),
|
|
Ns: common.Map(o.Ns, DNSRecordOptions.build),
|
|
Extra: common.Map(o.Extra, DNSRecordOptions.build),
|
|
}, nil
|
|
}
|
|
|
|
type DNSRecordOptions struct {
|
|
dns.RR
|
|
fromBase64 bool
|
|
}
|
|
|
|
func (o DNSRecordOptions) MarshalJSON() ([]byte, error) {
|
|
if o.fromBase64 {
|
|
buffer := buf.Get(dns.Len(o.RR))
|
|
defer buf.Put(buffer)
|
|
offset, err := dns.PackRR(o.RR, buffer, 0, nil, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(base64.StdEncoding.EncodeToString(buffer[:offset]))
|
|
}
|
|
return json.Marshal(o.RR.String())
|
|
}
|
|
|
|
func (o *DNSRecordOptions) UnmarshalJSON(data []byte) error {
|
|
var stringValue string
|
|
err := json.Unmarshal(data, &stringValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
binary, err := base64.StdEncoding.DecodeString(stringValue)
|
|
if err == nil {
|
|
return o.unmarshalBase64(binary)
|
|
}
|
|
record, err := dns.NewRR(stringValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
o.RR = record
|
|
return nil
|
|
}
|
|
|
|
func (o *DNSRecordOptions) unmarshalBase64(binary []byte) error {
|
|
record, _, err := dns.UnpackRR(binary, 0)
|
|
if err != nil {
|
|
return E.New("parse binary DNS record")
|
|
}
|
|
o.RR = record
|
|
o.fromBase64 = true
|
|
return nil
|
|
}
|
|
|
|
func (o DNSRecordOptions) build() dns.RR {
|
|
return o.RR
|
|
}
|