mirror of
https://github.com/bettercap/bettercap
synced 2025-07-05 20:42:09 -07:00
Add TLS support for DNS proxy, implement backwards compatible DNS record conversion.
This commit is contained in:
parent
a49d561dce
commit
43f1013f0d
8 changed files with 1603 additions and 145 deletions
|
@ -2,6 +2,10 @@ package dns_proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bettercap/bettercap/v2/session"
|
"github.com/bettercap/bettercap/v2/session"
|
||||||
|
"github.com/bettercap/bettercap/v2/tls"
|
||||||
|
|
||||||
|
"github.com/evilsocket/islazy/fs"
|
||||||
|
"github.com/evilsocket/islazy/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DnsProxy struct {
|
type DnsProxy struct {
|
||||||
|
@ -22,6 +26,10 @@ func (mod *DnsProxy) Configure() error {
|
||||||
var netProtocol string
|
var netProtocol string
|
||||||
var proxyPort int
|
var proxyPort int
|
||||||
var scriptPath string
|
var scriptPath string
|
||||||
|
var certFile string
|
||||||
|
var keyFile string
|
||||||
|
var whitelist string
|
||||||
|
var blacklist string
|
||||||
|
|
||||||
if mod.Running() {
|
if mod.Running() {
|
||||||
return session.ErrAlreadyStarted(mod.Name())
|
return session.ErrAlreadyStarted(mod.Name())
|
||||||
|
@ -29,21 +37,56 @@ func (mod *DnsProxy) Configure() error {
|
||||||
return err
|
return err
|
||||||
} else if err, address = mod.StringParam("dns.proxy.address"); err != nil {
|
} else if err, address = mod.StringParam("dns.proxy.address"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else if err, certFile = mod.StringParam("dns.proxy.certificate"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if certFile, err = fs.Expand(certFile); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err, keyFile = mod.StringParam("dns.proxy.key"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if keyFile, err = fs.Expand(keyFile); err != nil {
|
||||||
|
return err
|
||||||
} else if err, nameserver = mod.StringParam("dns.proxy.nameserver"); err != nil {
|
} else if err, nameserver = mod.StringParam("dns.proxy.nameserver"); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err, netProtocol = mod.StringParam("dns.proxy.networkprotocol"); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err, proxyPort = mod.IntParam("dns.proxy.port"); err != nil {
|
} else if err, proxyPort = mod.IntParam("dns.proxy.port"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else if err, netProtocol = mod.StringParam("dns.proxy.protocol"); err != nil {
|
||||||
|
return err
|
||||||
} else if err, doRedirect = mod.BoolParam("dns.proxy.redirect"); err != nil {
|
} else if err, doRedirect = mod.BoolParam("dns.proxy.redirect"); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err, scriptPath = mod.StringParam("dns.proxy.script"); err != nil {
|
} else if err, scriptPath = mod.StringParam("dns.proxy.script"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else if err, blacklist = mod.StringParam("dns.proxy.blacklist"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err, whitelist = mod.StringParam("dns.proxy.whitelist"); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
error := mod.proxy.Configure(address, dnsPort, doRedirect, nameserver, netProtocol, proxyPort, scriptPath)
|
mod.proxy.Blacklist = str.Comma(blacklist)
|
||||||
|
mod.proxy.Whitelist = str.Comma(whitelist)
|
||||||
|
|
||||||
return error
|
if netProtocol == "tcp-tls" {
|
||||||
|
if !fs.Exists(certFile) || !fs.Exists(keyFile) {
|
||||||
|
cfg, err := tls.CertConfigFromModule("dns.proxy", mod.SessionModule)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mod.Debug("%+v", cfg)
|
||||||
|
mod.Info("generating proxy certification authority TLS key to %s", keyFile)
|
||||||
|
mod.Info("generating proxy certification authority TLS certificate to %s", certFile)
|
||||||
|
if err := tls.Generate(cfg, certFile, keyFile, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mod.Info("loading proxy certification authority TLS key from %s", keyFile)
|
||||||
|
mod.Info("loading proxy certification authority TLS certificate from %s", certFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mod.proxy.Configure(address, dnsPort, doRedirect, nameserver, netProtocol,
|
||||||
|
proxyPort, scriptPath, certFile, keyFile)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mod *DnsProxy) Description() string {
|
func (mod *DnsProxy) Description() string {
|
||||||
|
@ -69,24 +112,42 @@ func NewDnsProxy(s *session.Session) *DnsProxy {
|
||||||
session.IPv4Validator,
|
session.IPv4Validator,
|
||||||
"Address to bind the DNS proxy to."))
|
"Address to bind the DNS proxy to."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.blacklist", "", "",
|
||||||
|
"Comma separated list of hostnames to skip while proxying (wildcard expressions can be used)."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.whitelist", "", "",
|
||||||
|
"Comma separated list of hostnames to proxy if the blacklist is used (wildcard expressions can be used)."))
|
||||||
|
|
||||||
mod.AddParam(session.NewStringParameter("dns.proxy.nameserver",
|
mod.AddParam(session.NewStringParameter("dns.proxy.nameserver",
|
||||||
"1.1.1.1",
|
"1.1.1.1",
|
||||||
session.IPv4Validator,
|
session.IPv4Validator,
|
||||||
"DNS resolver address."))
|
"DNS resolver address."))
|
||||||
|
|
||||||
mod.AddParam(session.NewStringParameter("dns.proxy.networkprotocol",
|
|
||||||
"udp",
|
|
||||||
"^(udp|tcp|tcp-tls)$",
|
|
||||||
"Network protocol for the DNS proxy server to use. Accepted values: udp, tcp, tcp-tls"))
|
|
||||||
|
|
||||||
mod.AddParam(session.NewIntParameter("dns.proxy.port",
|
mod.AddParam(session.NewIntParameter("dns.proxy.port",
|
||||||
"8053",
|
"8053",
|
||||||
"Port to bind the DNS proxy to."))
|
"Port to bind the DNS proxy to."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.protocol",
|
||||||
|
"udp",
|
||||||
|
"^(udp|tcp|tcp-tls)$",
|
||||||
|
"Network protocol for the DNS proxy server to use. Accepted values: udp, tcp, tcp-tls"))
|
||||||
|
|
||||||
mod.AddParam(session.NewBoolParameter("dns.proxy.redirect",
|
mod.AddParam(session.NewBoolParameter("dns.proxy.redirect",
|
||||||
"true",
|
"true",
|
||||||
"Enable or disable port redirection with iptables."))
|
"Enable or disable port redirection with iptables."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.certificate",
|
||||||
|
"~/.bettercap-ca.cert.pem",
|
||||||
|
"",
|
||||||
|
"DNS proxy certification authority TLS certificate file."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.key",
|
||||||
|
"~/.bettercap-ca.key.pem",
|
||||||
|
"",
|
||||||
|
"DNS proxy certification authority TLS key file."))
|
||||||
|
|
||||||
|
tls.CertConfigToModule("dns.proxy", &mod.SessionModule, tls.DefaultCloudflareDNSConfig)
|
||||||
|
|
||||||
mod.AddParam(session.NewStringParameter("dns.proxy.script",
|
mod.AddParam(session.NewStringParameter("dns.proxy.script",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
|
|
|
@ -2,7 +2,10 @@ package dns_proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -20,13 +23,17 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DNSProxy struct {
|
type DNSProxy struct {
|
||||||
Address string
|
|
||||||
Name string
|
Name string
|
||||||
|
Address string
|
||||||
|
Server *dns.Server
|
||||||
|
Redirection *firewall.Redirection
|
||||||
Nameserver string
|
Nameserver string
|
||||||
NetProtocol string
|
NetProtocol string
|
||||||
Redirection *firewall.Redirection
|
|
||||||
Script *DnsProxyScript
|
Script *DnsProxyScript
|
||||||
Server *dns.Server
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
|
Blacklist []string
|
||||||
|
Whitelist []string
|
||||||
Sess *session.Session
|
Sess *session.Session
|
||||||
|
|
||||||
doRedirect bool
|
doRedirect bool
|
||||||
|
@ -34,11 +41,13 @@ type DNSProxy struct {
|
||||||
tag string
|
tag string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DNSProxy) Configure(address string, dnsPort int, doRedirect bool, nameserver string, netProtocol string, proxyPort int, scriptPath string) error {
|
func (p *DNSProxy) Configure(address string, dnsPort int, doRedirect bool, nameserver string, netProtocol string, proxyPort int, scriptPath string, certFile string, keyFile string) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
p.Address = address
|
p.Address = address
|
||||||
p.doRedirect = doRedirect
|
p.doRedirect = doRedirect
|
||||||
|
p.CertFile = certFile
|
||||||
|
p.KeyFile = keyFile
|
||||||
|
|
||||||
if scriptPath != "" {
|
if scriptPath != "" {
|
||||||
if err, p.Script = LoadDnsProxyScript(scriptPath, p.Sess); err != nil {
|
if err, p.Script = LoadDnsProxyScript(scriptPath, p.Sess); err != nil {
|
||||||
|
@ -77,9 +86,10 @@ func (p *DNSProxy) Configure(address string, dnsPort int, doRedirect bool, names
|
||||||
}
|
}
|
||||||
res = p.onResponseFilter(req, res, clientIP)
|
res = p.onResponseFilter(req, res, clientIP)
|
||||||
if res == nil {
|
if res == nil {
|
||||||
p.Error("response filter returned nil")
|
p.Debug("response is nil")
|
||||||
m.SetRcode(req, dns.RcodeServerFailure)
|
m.SetRcode(req, dns.RcodeServerFailure)
|
||||||
w.WriteMsg(m)
|
w.WriteMsg(m)
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
if err := w.WriteMsg(res); err != nil {
|
if err := w.WriteMsg(res); err != nil {
|
||||||
p.Error("Error writing response: %s", err)
|
p.Error("Error writing response: %s", err)
|
||||||
|
@ -98,14 +108,35 @@ func (p *DNSProxy) Configure(address string, dnsPort int, doRedirect bool, names
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if netProtocol == "tcp-tls" && p.CertFile != "" && p.KeyFile != "" {
|
||||||
|
rawCert, _ := ioutil.ReadFile(p.CertFile)
|
||||||
|
rawKey, _ := ioutil.ReadFile(p.KeyFile)
|
||||||
|
ourCa, err := tls.X509KeyPair(rawCert, rawKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ourCa.Leaf, err = x509.ParseCertificate(ourCa.Certificate[0]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Server.TLSConfig = &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{ourCa},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if p.doRedirect {
|
if p.doRedirect {
|
||||||
if !p.Sess.Firewall.IsForwardingEnabled() {
|
if !p.Sess.Firewall.IsForwardingEnabled() {
|
||||||
p.Info("enabling forwarding.")
|
p.Info("enabling forwarding.")
|
||||||
p.Sess.Firewall.EnableForwarding(true)
|
p.Sess.Firewall.EnableForwarding(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
redirectProtocol := netProtocol
|
||||||
|
if redirectProtocol == "tcp-tls" {
|
||||||
|
redirectProtocol = "tcp"
|
||||||
|
}
|
||||||
p.Redirection = firewall.NewRedirection(p.Sess.Interface.Name(),
|
p.Redirection = firewall.NewRedirection(p.Sess.Interface.Name(),
|
||||||
netProtocol,
|
redirectProtocol,
|
||||||
dnsPort,
|
dnsPort,
|
||||||
p.Address,
|
p.Address,
|
||||||
proxyPort)
|
proxyPort)
|
||||||
|
|
|
@ -6,43 +6,60 @@ import (
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func shortenResourceRecords(records []string) []string {
|
func questionsToStrings(qs []dns.Question) []string {
|
||||||
shorterRecords := []string{}
|
questions := []string{}
|
||||||
for _, record := range records {
|
for _, q := range qs {
|
||||||
shorterRecord := strings.ReplaceAll(record, "\t", " ")
|
questions = append(questions, tabsToSpaces(q.String()))
|
||||||
shorterRecords = append(shorterRecords, shorterRecord)
|
|
||||||
}
|
}
|
||||||
return shorterRecords
|
return questions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DNSProxy) logRequestAction(j *JSQuery) {
|
func recordsToStrings(rrs []dns.RR) []string {
|
||||||
|
records := []string{}
|
||||||
|
for _, rr := range rrs {
|
||||||
|
records = append(records, tabsToSpaces(rr.String()))
|
||||||
|
}
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
|
func tabsToSpaces(s string) string {
|
||||||
|
return strings.ReplaceAll(s, "\t", " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) logRequestAction(m *dns.Msg, clientIP string) {
|
||||||
|
var questions []string
|
||||||
|
for _, q := range m.Question {
|
||||||
|
questions = append(questions, tabsToSpaces(q.String()))
|
||||||
|
}
|
||||||
p.Sess.Events.Add(p.Name+".spoofed-request", struct {
|
p.Sess.Events.Add(p.Name+".spoofed-request", struct {
|
||||||
Client string
|
Client string
|
||||||
Questions []string
|
Questions []string
|
||||||
}{
|
}{
|
||||||
j.Client["IP"],
|
clientIP,
|
||||||
shortenResourceRecords(j.Questions),
|
questions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DNSProxy) logResponseAction(j *JSQuery) {
|
func (p *DNSProxy) logResponseAction(m *dns.Msg, clientIP string) {
|
||||||
p.Sess.Events.Add(p.Name+".spoofed-response", struct {
|
p.Sess.Events.Add(p.Name+".spoofed-response", struct {
|
||||||
client string
|
client string
|
||||||
Extras []string
|
|
||||||
Answers []string
|
Answers []string
|
||||||
|
Extras []string
|
||||||
Nameservers []string
|
Nameservers []string
|
||||||
Questions []string
|
Questions []string
|
||||||
}{
|
}{
|
||||||
j.Client["IP"],
|
clientIP,
|
||||||
shortenResourceRecords(j.Extras),
|
recordsToStrings(m.Answer),
|
||||||
shortenResourceRecords(j.Answers),
|
recordsToStrings(m.Extra),
|
||||||
shortenResourceRecords(j.Nameservers),
|
recordsToStrings(m.Ns),
|
||||||
shortenResourceRecords(j.Questions),
|
questionsToStrings(m.Question),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DNSProxy) onRequestFilter(query *dns.Msg, clientIP string) (req, res *dns.Msg) {
|
func (p *DNSProxy) onRequestFilter(query *dns.Msg, clientIP string) (req, res *dns.Msg) {
|
||||||
p.Debug("< %s %s", clientIP, query.Question)
|
p.Debug("< %s %s",
|
||||||
|
clientIP,
|
||||||
|
strings.Join(questionsToStrings(query.Question), ","))
|
||||||
|
|
||||||
// do we have a proxy script?
|
// do we have a proxy script?
|
||||||
if p.Script == nil {
|
if p.Script == nil {
|
||||||
|
@ -53,12 +70,14 @@ func (p *DNSProxy) onRequestFilter(query *dns.Msg, clientIP string) (req, res *d
|
||||||
jsreq, jsres := p.Script.OnRequest(query, clientIP)
|
jsreq, jsres := p.Script.OnRequest(query, clientIP)
|
||||||
if jsreq != nil {
|
if jsreq != nil {
|
||||||
// the request has been changed by the script
|
// the request has been changed by the script
|
||||||
p.logRequestAction(jsreq)
|
req := jsreq.ToQuery()
|
||||||
return jsreq.ToQuery(), nil
|
p.logRequestAction(req, clientIP)
|
||||||
|
return req, nil
|
||||||
} else if jsres != nil {
|
} else if jsres != nil {
|
||||||
// a fake response has been returned by the script
|
// a fake response has been returned by the script
|
||||||
p.logResponseAction(jsres)
|
res := jsres.ToQuery()
|
||||||
return query, jsres.ToQuery()
|
p.logResponseAction(res, clientIP)
|
||||||
|
return query, res
|
||||||
}
|
}
|
||||||
|
|
||||||
return query, nil
|
return query, nil
|
||||||
|
@ -70,15 +89,21 @@ func (p *DNSProxy) onResponseFilter(req, res *dns.Msg, clientIP string) *dns.Msg
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Debug("> %s %s", clientIP, res.Answer)
|
p.Debug("> %s %s [%s] [%s] [%s]",
|
||||||
|
clientIP,
|
||||||
|
strings.Join(questionsToStrings(res.Question), ","),
|
||||||
|
strings.Join(recordsToStrings(res.Answer), ","),
|
||||||
|
strings.Join(recordsToStrings(res.Extra), ","),
|
||||||
|
strings.Join(recordsToStrings(res.Ns), ","))
|
||||||
|
|
||||||
// do we have a proxy script?
|
// do we have a proxy script?
|
||||||
if p.Script != nil {
|
if p.Script != nil {
|
||||||
_, jsres := p.Script.OnResponse(req, res, clientIP)
|
_, jsres := p.Script.OnResponse(req, res, clientIP)
|
||||||
if jsres != nil {
|
if jsres != nil {
|
||||||
// the response has been changed by the script
|
// the response has been changed by the script
|
||||||
p.logResponseAction(jsres)
|
res := jsres.ToQuery()
|
||||||
return jsres.ToQuery()
|
p.logResponseAction(res, clientIP)
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package dns_proxy
|
package dns_proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/bettercap/bettercap/v2/log"
|
"github.com/bettercap/bettercap/v2/log"
|
||||||
"github.com/bettercap/bettercap/v2/session"
|
"github.com/bettercap/bettercap/v2/session"
|
||||||
|
@ -11,17 +10,14 @@ import (
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
var whiteSpaceRegexp = regexp.MustCompile(`\s+`)
|
|
||||||
var stripWhiteSpaceRegexp = regexp.MustCompile(`^\s*(.*?)\s*$`)
|
|
||||||
|
|
||||||
type JSQuery struct {
|
type JSQuery struct {
|
||||||
Answers []string
|
Answers []map[string]interface{}
|
||||||
Client map[string]string
|
Client map[string]string
|
||||||
Compress bool `json:"-"`
|
Compress bool
|
||||||
Extras []string
|
Extras []map[string]interface{}
|
||||||
Header *JSQueryHeader
|
Header JSQueryHeader
|
||||||
Nameservers []string
|
Nameservers []map[string]interface{}
|
||||||
Questions []string
|
Questions []map[string]interface{}
|
||||||
|
|
||||||
refHash string
|
refHash string
|
||||||
}
|
}
|
||||||
|
@ -41,6 +37,11 @@ type JSQueryHeader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JSQuery) NewHash() string {
|
func (j *JSQuery) NewHash() string {
|
||||||
|
answers, _ := json.Marshal(j.Answers)
|
||||||
|
extras, _ := json.Marshal(j.Extras)
|
||||||
|
nameservers, _ := json.Marshal(j.Nameservers)
|
||||||
|
questions, _ := json.Marshal(j.Questions)
|
||||||
|
|
||||||
headerHash := fmt.Sprintf("%t.%t.%t.%d.%d.%d.%t.%t.%t.%t.%t",
|
headerHash := fmt.Sprintf("%t.%t.%t.%d.%d.%d.%t.%t.%t.%t.%t",
|
||||||
j.Header.AuthenticatedData,
|
j.Header.AuthenticatedData,
|
||||||
j.Header.Authoritative,
|
j.Header.Authoritative,
|
||||||
|
@ -53,50 +54,58 @@ func (j *JSQuery) NewHash() string {
|
||||||
j.Header.Response,
|
j.Header.Response,
|
||||||
j.Header.Truncated,
|
j.Header.Truncated,
|
||||||
j.Header.Zero)
|
j.Header.Zero)
|
||||||
|
|
||||||
hash := fmt.Sprintf("%s.%s.%t.%s.%s.%s.%s",
|
hash := fmt.Sprintf("%s.%s.%t.%s.%s.%s.%s",
|
||||||
strings.Join(j.Answers, ""),
|
answers,
|
||||||
j.Client["IP"],
|
j.Client["IP"],
|
||||||
j.Compress,
|
j.Compress,
|
||||||
strings.Join(j.Extras, ""),
|
extras,
|
||||||
headerHash,
|
headerHash,
|
||||||
strings.Join(j.Nameservers, ""),
|
nameservers,
|
||||||
strings.Join(j.Questions, ""))
|
questions)
|
||||||
|
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJSQuery(query *dns.Msg, clientIP string) *JSQuery {
|
func NewJSQuery(query *dns.Msg, clientIP string) (jsQuery *JSQuery) {
|
||||||
answers := []string{}
|
answers := make([]map[string]interface{}, len(query.Answer))
|
||||||
extras := []string{}
|
extras := make([]map[string]interface{}, len(query.Extra))
|
||||||
nameservers := []string{}
|
nameservers := make([]map[string]interface{}, len(query.Ns))
|
||||||
questions := []string{}
|
questions := make([]map[string]interface{}, len(query.Question))
|
||||||
|
|
||||||
header := &JSQueryHeader{
|
for i, rr := range query.Answer {
|
||||||
AuthenticatedData: query.MsgHdr.AuthenticatedData,
|
jsRecord, err := NewJSResourceRecord(rr)
|
||||||
Authoritative: query.MsgHdr.Authoritative,
|
if err != nil {
|
||||||
CheckingDisabled: query.MsgHdr.CheckingDisabled,
|
log.Error(err.Error())
|
||||||
Id: query.MsgHdr.Id,
|
continue
|
||||||
Opcode: query.MsgHdr.Opcode,
|
}
|
||||||
Rcode: query.MsgHdr.Rcode,
|
answers[i] = jsRecord
|
||||||
RecursionAvailable: query.MsgHdr.RecursionAvailable,
|
|
||||||
RecursionDesired: query.MsgHdr.RecursionDesired,
|
|
||||||
Response: query.MsgHdr.Response,
|
|
||||||
Truncated: query.MsgHdr.Truncated,
|
|
||||||
Zero: query.MsgHdr.Zero,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rr := range query.Answer {
|
for i, rr := range query.Extra {
|
||||||
answers = append(answers, rr.String())
|
jsRecord, err := NewJSResourceRecord(rr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
for _, rr := range query.Extra {
|
extras[i] = jsRecord
|
||||||
extras = append(extras, rr.String())
|
|
||||||
}
|
}
|
||||||
for _, rr := range query.Ns {
|
|
||||||
nameservers = append(nameservers, rr.String())
|
for i, rr := range query.Ns {
|
||||||
|
jsRecord, err := NewJSResourceRecord(rr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nameservers[i] = jsRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, question := range query.Question {
|
||||||
|
questions[i] = map[string]interface{}{
|
||||||
|
"Name": question.Name,
|
||||||
|
"Qtype": question.Qtype,
|
||||||
|
"Qclass": question.Qclass,
|
||||||
}
|
}
|
||||||
for _, q := range query.Question {
|
|
||||||
qType := dns.Type(q.Qtype).String()
|
|
||||||
qClass := dns.Class(q.Qclass).String()
|
|
||||||
questions = append(questions, fmt.Sprintf("%s\t%s\t%s", q.Name, qClass, qType))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clientMAC := ""
|
clientMAC := ""
|
||||||
|
@ -112,7 +121,19 @@ func NewJSQuery(query *dns.Msg, clientIP string) *JSQuery {
|
||||||
Client: client,
|
Client: client,
|
||||||
Compress: query.Compress,
|
Compress: query.Compress,
|
||||||
Extras: extras,
|
Extras: extras,
|
||||||
Header: header,
|
Header: JSQueryHeader{
|
||||||
|
AuthenticatedData: query.MsgHdr.AuthenticatedData,
|
||||||
|
Authoritative: query.MsgHdr.Authoritative,
|
||||||
|
CheckingDisabled: query.MsgHdr.CheckingDisabled,
|
||||||
|
Id: query.MsgHdr.Id,
|
||||||
|
Opcode: query.MsgHdr.Opcode,
|
||||||
|
Rcode: query.MsgHdr.Rcode,
|
||||||
|
RecursionAvailable: query.MsgHdr.RecursionAvailable,
|
||||||
|
RecursionDesired: query.MsgHdr.RecursionDesired,
|
||||||
|
Response: query.MsgHdr.Response,
|
||||||
|
Truncated: query.MsgHdr.Truncated,
|
||||||
|
Zero: query.MsgHdr.Zero,
|
||||||
|
},
|
||||||
Nameservers: nameservers,
|
Nameservers: nameservers,
|
||||||
Questions: questions,
|
Questions: questions,
|
||||||
}
|
}
|
||||||
|
@ -121,80 +142,43 @@ func NewJSQuery(query *dns.Msg, clientIP string) *JSQuery {
|
||||||
return jsquery
|
return jsquery
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringToClass(s string) (uint16, error) {
|
|
||||||
for i, dnsClass := range dns.ClassToString {
|
|
||||||
if s == dnsClass {
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("unkown DNS class (got %s)", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringToType(s string) (uint16, error) {
|
|
||||||
for i, dnsType := range dns.TypeToString {
|
|
||||||
if s == dnsType {
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("unkown DNS type (got %s)", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JSQuery) ToQuery() *dns.Msg {
|
func (j *JSQuery) ToQuery() *dns.Msg {
|
||||||
var answers []dns.RR
|
var answers []dns.RR
|
||||||
var extras []dns.RR
|
var extras []dns.RR
|
||||||
var nameservers []dns.RR
|
var nameservers []dns.RR
|
||||||
var questions []dns.Question
|
var questions []dns.Question
|
||||||
|
|
||||||
for _, s := range j.Answers {
|
for _, jsRR := range j.Answers {
|
||||||
rr, err := dns.NewRR(s)
|
rr, err := ToRR(jsRR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error parsing DNS answer resource record: %s", err.Error())
|
log.Error(err.Error())
|
||||||
return nil
|
continue
|
||||||
} else {
|
}
|
||||||
answers = append(answers, rr)
|
answers = append(answers, rr)
|
||||||
}
|
}
|
||||||
}
|
for _, jsRR := range j.Extras {
|
||||||
for _, s := range j.Extras {
|
rr, err := ToRR(jsRR)
|
||||||
rr, err := dns.NewRR(s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error parsing DNS extra resource record: %s", err.Error())
|
log.Error(err.Error())
|
||||||
return nil
|
continue
|
||||||
} else {
|
}
|
||||||
extras = append(extras, rr)
|
extras = append(extras, rr)
|
||||||
}
|
}
|
||||||
}
|
for _, jsRR := range j.Nameservers {
|
||||||
for _, s := range j.Nameservers {
|
rr, err := ToRR(jsRR)
|
||||||
rr, err := dns.NewRR(s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error parsing DNS nameserver resource record: %s", err.Error())
|
log.Error(err.Error())
|
||||||
return nil
|
continue
|
||||||
} else {
|
}
|
||||||
nameservers = append(nameservers, rr)
|
nameservers = append(nameservers, rr)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range j.Questions {
|
for _, jsQ := range j.Questions {
|
||||||
qStripped := stripWhiteSpaceRegexp.FindStringSubmatch(s)
|
questions = append(questions, dns.Question{
|
||||||
qParts := whiteSpaceRegexp.Split(qStripped[1], -1)
|
Name: jsPropToString(jsQ, "Name"),
|
||||||
|
Qtype: jsPropToUint16(jsQ, "Qtype"),
|
||||||
if len(qParts) != 3 {
|
Qclass: jsPropToUint16(jsQ, "Qclass"),
|
||||||
log.Error("invalid DNS question format: (got %s)", s)
|
})
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
qName := dns.Fqdn(qParts[0])
|
|
||||||
qClass, err := stringToClass(qParts[1])
|
|
||||||
if err != nil {
|
|
||||||
log.Error("error parsing DNS question class: %s", err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
qType, err := stringToType(qParts[2])
|
|
||||||
if err != nil {
|
|
||||||
log.Error("error parsing DNS question type: %s", err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
questions = append(questions, dns.Question{qName, qType, qClass})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query := &dns.Msg{
|
query := &dns.Msg{
|
||||||
|
|
1014
modules/dns_proxy/dns_proxy_js_record.go
Normal file
1014
modules/dns_proxy/dns_proxy_js_record.go
Normal file
File diff suppressed because it is too large
Load diff
208
modules/dns_proxy/dns_proxy_js_record_edns0.go
Normal file
208
modules/dns_proxy/dns_proxy_js_record_edns0.go
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
package dns_proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/v2/log"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewJSEDNS0(e dns.EDNS0) (jsEDNS0 map[string]interface{}, err error) {
|
||||||
|
option := e.Option()
|
||||||
|
|
||||||
|
jsEDNS0 = map[string]interface{}{
|
||||||
|
"Option": option,
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsVal map[string]interface{}
|
||||||
|
|
||||||
|
switch opt := e.(type) {
|
||||||
|
case *dns.EDNS0_LLQ:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Error": opt.Error,
|
||||||
|
"Id": opt.Id,
|
||||||
|
"LeaseLife": opt.LeaseLife,
|
||||||
|
"Opcode": opt.Opcode,
|
||||||
|
"Version": opt.Version,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_UL:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Lease": opt.Lease,
|
||||||
|
"KeyLease": opt.KeyLease,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_NSID:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Nsid": opt.Nsid,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_ESU:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Uri": opt.Uri,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_DAU:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"AlgCode": opt.AlgCode,
|
||||||
|
"Code": opt.Code,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_DHU:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"AlgCode": opt.AlgCode,
|
||||||
|
"Code": opt.Code,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_N3U:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"AlgCode": opt.AlgCode,
|
||||||
|
"Code": opt.Code,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_SUBNET:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Address": opt.Address.String(),
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Family": opt.Family,
|
||||||
|
"SourceNetmask": opt.SourceNetmask,
|
||||||
|
"SourceScope": opt.SourceScope,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_EXPIRE:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Empty": opt.Empty,
|
||||||
|
"Expire": opt.Expire,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_COOKIE:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Cookie": opt.Cookie,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_TCP_KEEPALIVE:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Length": opt.Length,
|
||||||
|
"Timeout": opt.Timeout,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_PADDING:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Padding": string(opt.Padding),
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_EDE:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"ExtraText": opt.ExtraText,
|
||||||
|
"InfoCode": opt.InfoCode,
|
||||||
|
}
|
||||||
|
case *dns.EDNS0_LOCAL:
|
||||||
|
jsVal = map[string]interface{}{
|
||||||
|
"Code": opt.Code,
|
||||||
|
"Data": string(opt.Data),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported EDNS0 option: %d", option)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsEDNS0["Value"] = jsVal
|
||||||
|
|
||||||
|
return jsEDNS0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToEDNS0(jsEDNS0 map[string]interface{}) (e dns.EDNS0, err error) {
|
||||||
|
option := jsPropToUint16(jsEDNS0, "Option")
|
||||||
|
|
||||||
|
jsVal := jsPropToMap(jsEDNS0, "Value")
|
||||||
|
|
||||||
|
switch option {
|
||||||
|
case dns.EDNS0LLQ:
|
||||||
|
e = &dns.EDNS0_LLQ{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Error: jsPropToUint16(jsVal, "Error"),
|
||||||
|
Id: jsPropToUint64(jsVal, "Id"),
|
||||||
|
LeaseLife: jsPropToUint32(jsVal, "LeaseLife"),
|
||||||
|
Opcode: jsPropToUint16(jsVal, "Opcode"),
|
||||||
|
Version: jsPropToUint16(jsVal, "Version"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0UL:
|
||||||
|
e = &dns.EDNS0_UL{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Lease: jsPropToUint32(jsVal, "Lease"),
|
||||||
|
KeyLease: jsPropToUint32(jsVal, "KeyLease"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0NSID:
|
||||||
|
e = &dns.EDNS0_NSID{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Nsid: jsPropToString(jsVal, "Nsid"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0ESU:
|
||||||
|
e = &dns.EDNS0_ESU{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Uri: jsPropToString(jsVal, "Uri"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0DAU:
|
||||||
|
e = &dns.EDNS0_DAU{
|
||||||
|
AlgCode: jsPropToUint8Array(jsVal, "AlgCode"),
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0DHU:
|
||||||
|
e = &dns.EDNS0_DHU{
|
||||||
|
AlgCode: jsPropToUint8Array(jsVal, "AlgCode"),
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0N3U:
|
||||||
|
e = &dns.EDNS0_N3U{
|
||||||
|
AlgCode: jsPropToUint8Array(jsVal, "AlgCode"),
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0SUBNET:
|
||||||
|
e = &dns.EDNS0_SUBNET{
|
||||||
|
Address: net.ParseIP(jsPropToString(jsVal, "Address")),
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Family: jsPropToUint16(jsVal, "Family"),
|
||||||
|
SourceNetmask: jsPropToUint8(jsVal, "SourceNetmask"),
|
||||||
|
SourceScope: jsPropToUint8(jsVal, "SourceScope"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0EXPIRE:
|
||||||
|
if empty, ok := jsVal["Empty"].(bool); !ok {
|
||||||
|
log.Error("invalid or missing EDNS0_EXPIRE.Empty bool value, skipping field.")
|
||||||
|
e = &dns.EDNS0_EXPIRE{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Expire: jsPropToUint32(jsVal, "Expire"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e = &dns.EDNS0_EXPIRE{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Expire: jsPropToUint32(jsVal, "Expire"),
|
||||||
|
Empty: empty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case dns.EDNS0COOKIE:
|
||||||
|
e = &dns.EDNS0_COOKIE{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Cookie: jsPropToString(jsVal, "Cookie"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0TCPKEEPALIVE:
|
||||||
|
e = &dns.EDNS0_TCP_KEEPALIVE{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Length: jsPropToUint16(jsVal, "Length"),
|
||||||
|
Timeout: jsPropToUint16(jsVal, "Timeout"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0PADDING:
|
||||||
|
e = &dns.EDNS0_PADDING{
|
||||||
|
Padding: []byte(jsPropToString(jsVal, "Padding")),
|
||||||
|
}
|
||||||
|
case dns.EDNS0EDE:
|
||||||
|
e = &dns.EDNS0_EDE{
|
||||||
|
ExtraText: jsPropToString(jsVal, "ExtraText"),
|
||||||
|
InfoCode: jsPropToUint16(jsVal, "InfoCode"),
|
||||||
|
}
|
||||||
|
case dns.EDNS0LOCALSTART, dns.EDNS0LOCALEND, 0x8000:
|
||||||
|
// _DO = 0x8000
|
||||||
|
e = &dns.EDNS0_LOCAL{
|
||||||
|
Code: jsPropToUint16(jsVal, "Code"),
|
||||||
|
Data: []byte(jsPropToString(jsVal, "Data")),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported EDNS0 option: %d", option)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e, nil
|
||||||
|
}
|
127
modules/dns_proxy/dns_proxy_js_record_svcb.go
Normal file
127
modules/dns_proxy/dns_proxy_js_record_svcb.go
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
package dns_proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/v2/log"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewJSSVCBKeyValue(kv dns.SVCBKeyValue) (map[string]interface{}, error) {
|
||||||
|
key := kv.Key()
|
||||||
|
|
||||||
|
jsKv := map[string]interface{}{
|
||||||
|
"Key": uint16(key),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := kv.(type) {
|
||||||
|
case *dns.SVCBAlpn:
|
||||||
|
jsKv["Alpn"] = v.Alpn
|
||||||
|
case *dns.SVCBNoDefaultAlpn:
|
||||||
|
break
|
||||||
|
case *dns.SVCBECHConfig:
|
||||||
|
jsKv["ECH"] = string(v.ECH)
|
||||||
|
case *dns.SVCBPort:
|
||||||
|
jsKv["Port"] = v.Port
|
||||||
|
case *dns.SVCBIPv4Hint:
|
||||||
|
ips := v.Hint
|
||||||
|
jsIps := make([]string, len(ips))
|
||||||
|
for i, ip := range ips {
|
||||||
|
jsIps[i] = ip.String()
|
||||||
|
}
|
||||||
|
jsKv["Hint"] = jsIps
|
||||||
|
case *dns.SVCBIPv6Hint:
|
||||||
|
ips := v.Hint
|
||||||
|
jsIps := make([]string, len(ips))
|
||||||
|
for i, ip := range ips {
|
||||||
|
jsIps[i] = ip.String()
|
||||||
|
}
|
||||||
|
jsKv["Hint"] = jsIps
|
||||||
|
case *dns.SVCBDoHPath:
|
||||||
|
jsKv["Template"] = v.Template
|
||||||
|
case *dns.SVCBOhttp:
|
||||||
|
break
|
||||||
|
case *dns.SVCBMandatory:
|
||||||
|
keys := v.Code
|
||||||
|
jsKeys := make([]uint16, len(keys))
|
||||||
|
for i, _key := range keys {
|
||||||
|
jsKeys[i] = uint16(_key)
|
||||||
|
}
|
||||||
|
jsKv["Code"] = jsKeys
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("error creating JSSVCBKeyValue: unknown key: %d", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsKv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToSVCBKeyValue(jsKv map[string]interface{}) (dns.SVCBKeyValue, error) {
|
||||||
|
var kv dns.SVCBKeyValue
|
||||||
|
|
||||||
|
key := dns.SVCBKey(jsPropToUint16(jsKv, "Key"))
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case dns.SVCB_ALPN:
|
||||||
|
kv = &dns.SVCBAlpn{
|
||||||
|
Alpn: jsPropToStringArray(jsKv, "Value"),
|
||||||
|
}
|
||||||
|
case dns.SVCB_NO_DEFAULT_ALPN:
|
||||||
|
kv = &dns.SVCBNoDefaultAlpn{}
|
||||||
|
case dns.SVCB_ECHCONFIG:
|
||||||
|
kv = &dns.SVCBECHConfig{
|
||||||
|
ECH: []byte(jsPropToString(jsKv, "Value")),
|
||||||
|
}
|
||||||
|
case dns.SVCB_PORT:
|
||||||
|
kv = &dns.SVCBPort{
|
||||||
|
Port: jsPropToUint16(jsKv, "Value"),
|
||||||
|
}
|
||||||
|
case dns.SVCB_IPV4HINT:
|
||||||
|
jsIps := jsPropToStringArray(jsKv, "Value")
|
||||||
|
var ips []net.IP
|
||||||
|
for _, jsIp := range jsIps {
|
||||||
|
ip := net.ParseIP(jsIp)
|
||||||
|
if ip == nil {
|
||||||
|
log.Error("error converting to SVCBKeyValue: invalid IPv4Hint IP: %s", jsIp)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ips = append(ips, ip)
|
||||||
|
}
|
||||||
|
kv = &dns.SVCBIPv4Hint{
|
||||||
|
Hint: ips,
|
||||||
|
}
|
||||||
|
case dns.SVCB_IPV6HINT:
|
||||||
|
jsIps := jsPropToStringArray(jsKv, "Value")
|
||||||
|
var ips []net.IP
|
||||||
|
for _, jsIp := range jsIps {
|
||||||
|
ip := net.ParseIP(jsIp)
|
||||||
|
if ip == nil {
|
||||||
|
log.Error("error converting to SVCBKeyValue: invalid IPv6Hint IP: %s", jsIp)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ips = append(ips, ip)
|
||||||
|
}
|
||||||
|
kv = &dns.SVCBIPv6Hint{
|
||||||
|
Hint: ips,
|
||||||
|
}
|
||||||
|
case dns.SVCB_DOHPATH:
|
||||||
|
kv = &dns.SVCBDoHPath{
|
||||||
|
Template: jsPropToString(jsKv, "Value"),
|
||||||
|
}
|
||||||
|
case dns.SVCB_OHTTP:
|
||||||
|
kv = &dns.SVCBOhttp{}
|
||||||
|
case dns.SVCB_MANDATORY:
|
||||||
|
v := jsPropToUint16Array(jsKv, "Value")
|
||||||
|
keys := make([]dns.SVCBKey, len(v))
|
||||||
|
for i, jsKey := range v {
|
||||||
|
keys[i] = dns.SVCBKey(jsKey)
|
||||||
|
}
|
||||||
|
kv = &dns.SVCBMandatory{
|
||||||
|
Code: keys,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("error converting to dns.SVCBKeyValue: unknown key: %d", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return kv, nil
|
||||||
|
}
|
|
@ -40,6 +40,14 @@ var (
|
||||||
OrganizationalUnit: "https://certs.godaddy.com/repository/",
|
OrganizationalUnit: "https://certs.godaddy.com/repository/",
|
||||||
CommonName: "Go Daddy Secure Certificate Authority - G2",
|
CommonName: "Go Daddy Secure Certificate Authority - G2",
|
||||||
}
|
}
|
||||||
|
DefaultCloudflareDNSConfig = CertConfig{
|
||||||
|
Bits: 4096,
|
||||||
|
Country: "US",
|
||||||
|
Locality: "San Francisco",
|
||||||
|
Organization: "Cloudflare, Inc.",
|
||||||
|
OrganizationalUnit: "",
|
||||||
|
CommonName: "cloudflare-dns.com",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func CertConfigToModule(prefix string, m *session.SessionModule, defaults CertConfig) {
|
func CertConfigToModule(prefix string, m *session.SessionModule, defaults CertConfig) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue