mirror of
https://github.com/bettercap/bettercap
synced 2025-08-20 05:23:19 -07:00
commit
906162e5fa
10 changed files with 2256 additions and 0 deletions
185
modules/dns_proxy/dns_proxy.go
Normal file
185
modules/dns_proxy/dns_proxy.go
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
package dns_proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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 {
|
||||||
|
session.SessionModule
|
||||||
|
proxy *DNSProxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *DnsProxy) Author() string {
|
||||||
|
return "Yarwin Kolff <@buffermet>"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *DnsProxy) Configure() error {
|
||||||
|
var err error
|
||||||
|
var address string
|
||||||
|
var dnsPort int
|
||||||
|
var doRedirect bool
|
||||||
|
var nameserver string
|
||||||
|
var netProtocol string
|
||||||
|
var proxyPort int
|
||||||
|
var scriptPath string
|
||||||
|
var certFile string
|
||||||
|
var keyFile string
|
||||||
|
var whitelist string
|
||||||
|
var blacklist string
|
||||||
|
|
||||||
|
if mod.Running() {
|
||||||
|
return session.ErrAlreadyStarted(mod.Name())
|
||||||
|
} else if err, dnsPort = mod.IntParam("dns.port"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err, address = mod.StringParam("dns.proxy.address"); err != nil {
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
} else if err, proxyPort = mod.IntParam("dns.proxy.port"); err != nil {
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
} else if err, scriptPath = mod.StringParam("dns.proxy.script"); err != nil {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
mod.proxy.Blacklist = str.Comma(blacklist)
|
||||||
|
mod.proxy.Whitelist = str.Comma(whitelist)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return "A full featured DNS proxy that can be used to manipulate DNS traffic."
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *DnsProxy) Name() string {
|
||||||
|
return "dns.proxy"
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDnsProxy(s *session.Session) *DnsProxy {
|
||||||
|
mod := &DnsProxy{
|
||||||
|
SessionModule: session.NewSessionModule("dns.proxy", s),
|
||||||
|
proxy: NewDNSProxy(s, "dns.proxy"),
|
||||||
|
}
|
||||||
|
|
||||||
|
mod.AddParam(session.NewIntParameter("dns.port",
|
||||||
|
"53",
|
||||||
|
"DNS port to redirect when the proxy is activated."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.address",
|
||||||
|
session.ParamIfaceAddress,
|
||||||
|
session.IPv4Validator,
|
||||||
|
"Address to bind the DNS proxy to."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.blacklist", "", "",
|
||||||
|
"Comma separated list of client IPs to skip while proxying (wildcard allowed)."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.whitelist", "", "",
|
||||||
|
"Comma separated list of client IPs to proxy if the blacklist is used."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("dns.proxy.nameserver",
|
||||||
|
"1.1.1.1",
|
||||||
|
session.IPv4Validator,
|
||||||
|
"DNS resolver address."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewIntParameter("dns.proxy.port",
|
||||||
|
"8053",
|
||||||
|
"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",
|
||||||
|
"true",
|
||||||
|
"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",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"Path of a JS proxy script."))
|
||||||
|
|
||||||
|
mod.AddHandler(session.NewModuleHandler("dns.proxy on", "",
|
||||||
|
"Start the DNS proxy.",
|
||||||
|
func(args []string) error {
|
||||||
|
return mod.Start()
|
||||||
|
}))
|
||||||
|
|
||||||
|
mod.AddHandler(session.NewModuleHandler("dns.proxy off", "",
|
||||||
|
"Stop the DNS proxy.",
|
||||||
|
func(args []string) error {
|
||||||
|
return mod.Stop()
|
||||||
|
}))
|
||||||
|
|
||||||
|
return mod
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *DnsProxy) Start() error {
|
||||||
|
if err := mod.Configure(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mod.SetRunning(true, func() {
|
||||||
|
mod.proxy.Start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mod *DnsProxy) Stop() error {
|
||||||
|
return mod.SetRunning(false, func() {
|
||||||
|
mod.proxy.Stop()
|
||||||
|
})
|
||||||
|
}
|
241
modules/dns_proxy/dns_proxy_base.go
Normal file
241
modules/dns_proxy/dns_proxy_base.go
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
package dns_proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/v2/firewall"
|
||||||
|
"github.com/bettercap/bettercap/v2/session"
|
||||||
|
"github.com/evilsocket/islazy/log"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dialTimeout = 2 * time.Second
|
||||||
|
readTimeout = 2 * time.Second
|
||||||
|
writeTimeout = 2 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type DNSProxy struct {
|
||||||
|
Name string
|
||||||
|
Address string
|
||||||
|
Server *dns.Server
|
||||||
|
Redirection *firewall.Redirection
|
||||||
|
Nameserver string
|
||||||
|
NetProtocol string
|
||||||
|
Script *DnsProxyScript
|
||||||
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
|
Blacklist []string
|
||||||
|
Whitelist []string
|
||||||
|
Sess *session.Session
|
||||||
|
|
||||||
|
doRedirect bool
|
||||||
|
isRunning bool
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) shouldProxy(clientIP string) bool {
|
||||||
|
// check if this client is in the whitelist
|
||||||
|
for _, ip := range p.Whitelist {
|
||||||
|
if clientIP == ip {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this client is in the blacklist
|
||||||
|
for _, ip := range p.Blacklist {
|
||||||
|
if ip == "*" || clientIP == ip {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
p.Address = address
|
||||||
|
p.doRedirect = doRedirect
|
||||||
|
p.CertFile = certFile
|
||||||
|
p.KeyFile = keyFile
|
||||||
|
|
||||||
|
if scriptPath != "" {
|
||||||
|
if err, p.Script = LoadDnsProxyScript(scriptPath, p.Sess); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
p.Debug("proxy script %s loaded.", scriptPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsClient := dns.Client{
|
||||||
|
DialTimeout: dialTimeout,
|
||||||
|
Net: netProtocol,
|
||||||
|
ReadTimeout: readTimeout,
|
||||||
|
WriteTimeout: writeTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
resolverAddr := fmt.Sprintf("%s:%d", nameserver, dnsPort)
|
||||||
|
|
||||||
|
handler := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetReply(req)
|
||||||
|
|
||||||
|
clientIP := strings.Split(w.RemoteAddr().String(), ":")[0]
|
||||||
|
|
||||||
|
req, res := p.onRequestFilter(req, clientIP)
|
||||||
|
if res == nil {
|
||||||
|
// unused var is time til res
|
||||||
|
res, _, err := dnsClient.Exchange(req, resolverAddr)
|
||||||
|
if err != nil {
|
||||||
|
p.Debug("error while resolving DNS query: %s", err.Error())
|
||||||
|
m.SetRcode(req, dns.RcodeServerFailure)
|
||||||
|
w.WriteMsg(m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res = p.onResponseFilter(req, res, clientIP)
|
||||||
|
if res == nil {
|
||||||
|
p.Debug("response is nil")
|
||||||
|
m.SetRcode(req, dns.RcodeServerFailure)
|
||||||
|
w.WriteMsg(m)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if err := w.WriteMsg(res); err != nil {
|
||||||
|
p.Error("Error writing response: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := w.WriteMsg(res); err != nil {
|
||||||
|
p.Error("Error writing response: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
p.Server = &dns.Server{
|
||||||
|
Addr: fmt.Sprintf("%s:%d", address, proxyPort),
|
||||||
|
Net: netProtocol,
|
||||||
|
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.Sess.Firewall.IsForwardingEnabled() {
|
||||||
|
p.Info("enabling forwarding.")
|
||||||
|
p.Sess.Firewall.EnableForwarding(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectProtocol := netProtocol
|
||||||
|
if redirectProtocol == "tcp-tls" {
|
||||||
|
redirectProtocol = "tcp"
|
||||||
|
}
|
||||||
|
p.Redirection = firewall.NewRedirection(p.Sess.Interface.Name(),
|
||||||
|
redirectProtocol,
|
||||||
|
dnsPort,
|
||||||
|
p.Address,
|
||||||
|
proxyPort)
|
||||||
|
|
||||||
|
if err := p.Sess.Firewall.EnableRedirection(p.Redirection, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Debug("applied redirection %s", p.Redirection.String())
|
||||||
|
} else {
|
||||||
|
p.Warning("port redirection disabled, the proxy must be set manually to work")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Sess.UnkCmdCallback = func(cmd string) bool {
|
||||||
|
if p.Script != nil {
|
||||||
|
return p.Script.OnCommand(cmd)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) dnsWorker() error {
|
||||||
|
p.isRunning = true
|
||||||
|
return p.Server.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) Debug(format string, args ...interface{}) {
|
||||||
|
p.Sess.Events.Log(log.DEBUG, p.tag+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) Info(format string, args ...interface{}) {
|
||||||
|
p.Sess.Events.Log(log.INFO, p.tag+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) Warning(format string, args ...interface{}) {
|
||||||
|
p.Sess.Events.Log(log.WARNING, p.tag+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) Error(format string, args ...interface{}) {
|
||||||
|
p.Sess.Events.Log(log.ERROR, p.tag+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) Fatal(format string, args ...interface{}) {
|
||||||
|
p.Sess.Events.Log(log.FATAL, p.tag+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSProxy(s *session.Session, tag string) *DNSProxy {
|
||||||
|
p := &DNSProxy{
|
||||||
|
Name: "dns.proxy",
|
||||||
|
Sess: s,
|
||||||
|
Server: nil,
|
||||||
|
doRedirect: true,
|
||||||
|
tag: session.AsTag(tag),
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) Start() {
|
||||||
|
go func() {
|
||||||
|
p.Info("started on %s", p.Server.Addr)
|
||||||
|
|
||||||
|
err := p.dnsWorker()
|
||||||
|
// TODO: check the dns server closed error
|
||||||
|
if err != nil && err.Error() != "dns: Server closed" {
|
||||||
|
p.Fatal("%s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) Stop() error {
|
||||||
|
if p.doRedirect && p.Redirection != nil {
|
||||||
|
p.Debug("disabling redirection %s", p.Redirection.String())
|
||||||
|
if err := p.Sess.Firewall.EnableRedirection(p.Redirection, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Redirection = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Sess.UnkCmdCallback = nil
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
return p.Server.ShutdownContext(ctx)
|
||||||
|
}
|
113
modules/dns_proxy/dns_proxy_base_filters.go
Normal file
113
modules/dns_proxy/dns_proxy_base_filters.go
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
package dns_proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func questionsToStrings(qs []dns.Question) []string {
|
||||||
|
questions := []string{}
|
||||||
|
for _, q := range qs {
|
||||||
|
questions = append(questions, tabsToSpaces(q.String()))
|
||||||
|
}
|
||||||
|
return questions
|
||||||
|
}
|
||||||
|
|
||||||
|
func recordsToStrings(rrs []dns.RR) []string {
|
||||||
|
records := []string{}
|
||||||
|
for _, rr := range rrs {
|
||||||
|
if rr != nil {
|
||||||
|
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) {
|
||||||
|
p.Sess.Events.Add(p.Name+".spoofed-request", struct {
|
||||||
|
Client string
|
||||||
|
Questions []string
|
||||||
|
}{
|
||||||
|
clientIP,
|
||||||
|
questionsToStrings(m.Question),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) logResponseAction(m *dns.Msg, clientIP string) {
|
||||||
|
p.Sess.Events.Add(p.Name+".spoofed-response", struct {
|
||||||
|
client string
|
||||||
|
Answers []string
|
||||||
|
Extras []string
|
||||||
|
Nameservers []string
|
||||||
|
Questions []string
|
||||||
|
}{
|
||||||
|
clientIP,
|
||||||
|
recordsToStrings(m.Answer),
|
||||||
|
recordsToStrings(m.Extra),
|
||||||
|
recordsToStrings(m.Ns),
|
||||||
|
questionsToStrings(m.Question),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) onRequestFilter(query *dns.Msg, clientIP string) (req, res *dns.Msg) {
|
||||||
|
if p.shouldProxy(clientIP) {
|
||||||
|
p.Debug("< %s q[%s]",
|
||||||
|
clientIP,
|
||||||
|
strings.Join(questionsToStrings(query.Question), ","))
|
||||||
|
|
||||||
|
// do we have a proxy script?
|
||||||
|
if p.Script == nil {
|
||||||
|
return query, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the module OnRequest callback if defined
|
||||||
|
jsreq, jsres := p.Script.OnRequest(query, clientIP)
|
||||||
|
if jsreq != nil {
|
||||||
|
// the request has been changed by the script
|
||||||
|
req := jsreq.ToQuery()
|
||||||
|
p.logRequestAction(req, clientIP)
|
||||||
|
return req, nil
|
||||||
|
} else if jsres != nil {
|
||||||
|
// a fake response has been returned by the script
|
||||||
|
res := jsres.ToQuery()
|
||||||
|
p.logResponseAction(res, clientIP)
|
||||||
|
return query, res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DNSProxy) onResponseFilter(req, res *dns.Msg, clientIP string) *dns.Msg {
|
||||||
|
if p.shouldProxy(clientIP) {
|
||||||
|
// sometimes it happens ¯\_(ツ)_/¯
|
||||||
|
if res == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Debug("> %s q[%s] a[%s] e[%s] n[%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?
|
||||||
|
if p.Script != nil {
|
||||||
|
_, jsres := p.Script.OnResponse(req, res, clientIP)
|
||||||
|
if jsres != nil {
|
||||||
|
// the response has been changed by the script
|
||||||
|
res := jsres.ToQuery()
|
||||||
|
p.logResponseAction(res, clientIP)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
295
modules/dns_proxy/dns_proxy_js_query.go
Normal file
295
modules/dns_proxy/dns_proxy_js_query.go
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
package dns_proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/v2/log"
|
||||||
|
"github.com/bettercap/bettercap/v2/session"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSQuery struct {
|
||||||
|
Answers []map[string]interface{}
|
||||||
|
Client map[string]string
|
||||||
|
Compress bool
|
||||||
|
Extras []map[string]interface{}
|
||||||
|
Header JSQueryHeader
|
||||||
|
Nameservers []map[string]interface{}
|
||||||
|
Questions []map[string]interface{}
|
||||||
|
|
||||||
|
refHash string
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSQueryHeader struct {
|
||||||
|
AuthenticatedData bool
|
||||||
|
Authoritative bool
|
||||||
|
CheckingDisabled bool
|
||||||
|
Id uint16
|
||||||
|
Opcode int
|
||||||
|
Rcode int
|
||||||
|
RecursionAvailable bool
|
||||||
|
RecursionDesired bool
|
||||||
|
Response bool
|
||||||
|
Truncated bool
|
||||||
|
Zero bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToMap(obj map[string]interface{}, key string) map[string]interface{} {
|
||||||
|
if v, ok := obj[key].(map[string]interface{}); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to map[string]interface{} where key is: %s", key)
|
||||||
|
return map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToMapArray(obj map[string]interface{}, key string) []map[string]interface{} {
|
||||||
|
if v, ok := obj[key].([]map[string]interface{}); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to []map[string]interface{} where key is: %s", key)
|
||||||
|
return []map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToString(obj map[string]interface{}, key string) string {
|
||||||
|
if v, ok := obj[key].(string); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to string where key is: %s", key)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToStringArray(obj map[string]interface{}, key string) []string {
|
||||||
|
if v, ok := obj[key].([]string); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to []string where key is: %s", key)
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToUint8(obj map[string]interface{}, key string) uint8 {
|
||||||
|
if v, ok := obj[key].(uint8); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to uint8 where key is: %s", key)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToUint8Array(obj map[string]interface{}, key string) []uint8 {
|
||||||
|
if v, ok := obj[key].([]uint8); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to []uint8 where key is: %s", key)
|
||||||
|
return []uint8{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToUint16(obj map[string]interface{}, key string) uint16 {
|
||||||
|
if v, ok := obj[key].(uint16); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to uint16 where key is: %s", key)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToUint16Array(obj map[string]interface{}, key string) []uint16 {
|
||||||
|
if v, ok := obj[key].([]uint16); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to []uint16 where key is: %s", key)
|
||||||
|
return []uint16{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToUint32(obj map[string]interface{}, key string) uint32 {
|
||||||
|
if v, ok := obj[key].(uint32); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to uint32 where key is: %s", key)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsPropToUint64(obj map[string]interface{}, key string) uint64 {
|
||||||
|
if v, ok := obj[key].(uint64); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
log.Debug("error converting JS property to uint64 where key is: %s", key)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
j.Header.AuthenticatedData,
|
||||||
|
j.Header.Authoritative,
|
||||||
|
j.Header.CheckingDisabled,
|
||||||
|
j.Header.Id,
|
||||||
|
j.Header.Opcode,
|
||||||
|
j.Header.Rcode,
|
||||||
|
j.Header.RecursionAvailable,
|
||||||
|
j.Header.RecursionDesired,
|
||||||
|
j.Header.Response,
|
||||||
|
j.Header.Truncated,
|
||||||
|
j.Header.Zero)
|
||||||
|
|
||||||
|
hash := fmt.Sprintf("%s.%s.%t.%s.%s.%s.%s",
|
||||||
|
answers,
|
||||||
|
j.Client["IP"],
|
||||||
|
j.Compress,
|
||||||
|
extras,
|
||||||
|
headerHash,
|
||||||
|
nameservers,
|
||||||
|
questions)
|
||||||
|
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJSQuery(query *dns.Msg, clientIP string) (jsQuery *JSQuery) {
|
||||||
|
answers := make([]map[string]interface{}, len(query.Answer))
|
||||||
|
extras := make([]map[string]interface{}, len(query.Extra))
|
||||||
|
nameservers := make([]map[string]interface{}, len(query.Ns))
|
||||||
|
questions := make([]map[string]interface{}, len(query.Question))
|
||||||
|
|
||||||
|
for i, rr := range query.Answer {
|
||||||
|
jsRecord, err := NewJSResourceRecord(rr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
answers[i] = jsRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, rr := range query.Extra {
|
||||||
|
jsRecord, err := NewJSResourceRecord(rr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
extras[i] = jsRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientMAC := ""
|
||||||
|
clientAlias := ""
|
||||||
|
if endpoint := session.I.Lan.GetByIp(clientIP); endpoint != nil {
|
||||||
|
clientMAC = endpoint.HwAddress
|
||||||
|
clientAlias = endpoint.Alias
|
||||||
|
}
|
||||||
|
client := map[string]string{"IP": clientIP, "MAC": clientMAC, "Alias": clientAlias}
|
||||||
|
|
||||||
|
jsquery := &JSQuery{
|
||||||
|
Answers: answers,
|
||||||
|
Client: client,
|
||||||
|
Compress: query.Compress,
|
||||||
|
Extras: extras,
|
||||||
|
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,
|
||||||
|
Questions: questions,
|
||||||
|
}
|
||||||
|
jsquery.UpdateHash()
|
||||||
|
|
||||||
|
return jsquery
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JSQuery) ToQuery() *dns.Msg {
|
||||||
|
var answers []dns.RR
|
||||||
|
var extras []dns.RR
|
||||||
|
var nameservers []dns.RR
|
||||||
|
var questions []dns.Question
|
||||||
|
|
||||||
|
for _, jsRR := range j.Answers {
|
||||||
|
rr, err := ToRR(jsRR)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
answers = append(answers, rr)
|
||||||
|
}
|
||||||
|
for _, jsRR := range j.Extras {
|
||||||
|
rr, err := ToRR(jsRR)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
extras = append(extras, rr)
|
||||||
|
}
|
||||||
|
for _, jsRR := range j.Nameservers {
|
||||||
|
rr, err := ToRR(jsRR)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nameservers = append(nameservers, rr)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, jsQ := range j.Questions {
|
||||||
|
questions = append(questions, dns.Question{
|
||||||
|
Name: jsPropToString(jsQ, "Name"),
|
||||||
|
Qtype: jsPropToUint16(jsQ, "Qtype"),
|
||||||
|
Qclass: jsPropToUint16(jsQ, "Qclass"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
query := &dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Id: j.Header.Id,
|
||||||
|
Response: j.Header.Response,
|
||||||
|
Opcode: j.Header.Opcode,
|
||||||
|
Authoritative: j.Header.Authoritative,
|
||||||
|
Truncated: j.Header.Truncated,
|
||||||
|
RecursionDesired: j.Header.RecursionDesired,
|
||||||
|
RecursionAvailable: j.Header.RecursionAvailable,
|
||||||
|
Zero: j.Header.Zero,
|
||||||
|
AuthenticatedData: j.Header.AuthenticatedData,
|
||||||
|
CheckingDisabled: j.Header.CheckingDisabled,
|
||||||
|
Rcode: j.Header.Rcode,
|
||||||
|
},
|
||||||
|
Compress: j.Compress,
|
||||||
|
Question: questions,
|
||||||
|
Answer: answers,
|
||||||
|
Ns: nameservers,
|
||||||
|
Extra: extras,
|
||||||
|
}
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JSQuery) UpdateHash() {
|
||||||
|
j.refHash = j.NewHash()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JSQuery) WasModified() bool {
|
||||||
|
// check if any of the fields has been changed
|
||||||
|
return j.NewHash() != j.refHash
|
||||||
|
}
|
951
modules/dns_proxy/dns_proxy_js_record.go
Normal file
951
modules/dns_proxy/dns_proxy_js_record.go
Normal file
|
@ -0,0 +1,951 @@
|
||||||
|
package dns_proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/v2/log"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewJSResourceRecord(rr dns.RR) (jsRecord map[string]interface{}, err error) {
|
||||||
|
header := rr.Header()
|
||||||
|
|
||||||
|
jsRecord = map[string]interface{}{
|
||||||
|
"Header": map[string]interface{}{
|
||||||
|
"Class": header.Class,
|
||||||
|
"Name": header.Name,
|
||||||
|
"Rrtype": header.Rrtype,
|
||||||
|
"Ttl": header.Ttl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
switch rr := rr.(type) {
|
||||||
|
case *dns.A:
|
||||||
|
jsRecord["A"] = rr.A.String()
|
||||||
|
case *dns.AAAA:
|
||||||
|
jsRecord["AAAA"] = rr.AAAA.String()
|
||||||
|
case *dns.APL:
|
||||||
|
jsPrefixes := make([]map[string]interface{}, len(rr.Prefixes))
|
||||||
|
for i, v := range rr.Prefixes {
|
||||||
|
jsPrefixes[i] = map[string]interface{}{
|
||||||
|
"Negation": v.Negation,
|
||||||
|
"Network": v.Network.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsRecord["Prefixes"] = jsPrefixes
|
||||||
|
case *dns.CNAME:
|
||||||
|
jsRecord["Target"] = rr.Target
|
||||||
|
case *dns.MB:
|
||||||
|
jsRecord["Mb"] = rr.Mb
|
||||||
|
case *dns.MD:
|
||||||
|
jsRecord["Md"] = rr.Md
|
||||||
|
case *dns.MF:
|
||||||
|
jsRecord["Mf"] = rr.Mf
|
||||||
|
case *dns.MG:
|
||||||
|
jsRecord["Mg"] = rr.Mg
|
||||||
|
case *dns.MR:
|
||||||
|
jsRecord["Mr"] = rr.Mr
|
||||||
|
case *dns.MX:
|
||||||
|
jsRecord["Mx"] = rr.Mx
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
case *dns.NULL:
|
||||||
|
jsRecord["Data"] = rr.Data
|
||||||
|
case *dns.SOA:
|
||||||
|
jsRecord["Expire"] = rr.Expire
|
||||||
|
jsRecord["Minttl"] = rr.Minttl
|
||||||
|
jsRecord["Ns"] = rr.Ns
|
||||||
|
jsRecord["Refresh"] = rr.Refresh
|
||||||
|
jsRecord["Retry"] = rr.Retry
|
||||||
|
jsRecord["Mbox"] = rr.Mbox
|
||||||
|
jsRecord["Serial"] = rr.Serial
|
||||||
|
case *dns.TXT:
|
||||||
|
jsRecord["Txt"] = rr.Txt
|
||||||
|
case *dns.SRV:
|
||||||
|
jsRecord["Port"] = rr.Port
|
||||||
|
jsRecord["Priority"] = rr.Priority
|
||||||
|
jsRecord["Target"] = rr.Target
|
||||||
|
jsRecord["Weight"] = rr.Weight
|
||||||
|
case *dns.PTR:
|
||||||
|
jsRecord["Ptr"] = rr.Ptr
|
||||||
|
case *dns.NS:
|
||||||
|
jsRecord["Ns"] = rr.Ns
|
||||||
|
case *dns.DNAME:
|
||||||
|
jsRecord["Target"] = rr.Target
|
||||||
|
case *dns.AFSDB:
|
||||||
|
jsRecord["Subtype"] = rr.Subtype
|
||||||
|
jsRecord["Hostname"] = rr.Hostname
|
||||||
|
case *dns.CAA:
|
||||||
|
jsRecord["Flag"] = rr.Flag
|
||||||
|
jsRecord["Tag"] = rr.Tag
|
||||||
|
jsRecord["Value"] = rr.Value
|
||||||
|
case *dns.HINFO:
|
||||||
|
jsRecord["Cpu"] = rr.Cpu
|
||||||
|
jsRecord["Os"] = rr.Os
|
||||||
|
case *dns.MINFO:
|
||||||
|
jsRecord["Email"] = rr.Email
|
||||||
|
jsRecord["Rmail"] = rr.Rmail
|
||||||
|
case *dns.ISDN:
|
||||||
|
jsRecord["Address"] = rr.Address
|
||||||
|
jsRecord["SubAddress"] = rr.SubAddress
|
||||||
|
case *dns.KX:
|
||||||
|
jsRecord["Exchanger"] = rr.Exchanger
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
case *dns.LOC:
|
||||||
|
jsRecord["Altitude"] = rr.Altitude
|
||||||
|
jsRecord["HorizPre"] = rr.HorizPre
|
||||||
|
jsRecord["Latitude"] = rr.Latitude
|
||||||
|
jsRecord["Longitude"] = rr.Longitude
|
||||||
|
jsRecord["Size"] = rr.Size
|
||||||
|
jsRecord["Version"] = rr.Version
|
||||||
|
jsRecord["VertPre"] = rr.VertPre
|
||||||
|
case *dns.SSHFP:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["FingerPrint"] = rr.FingerPrint
|
||||||
|
jsRecord["Type"] = rr.Type
|
||||||
|
case *dns.TLSA:
|
||||||
|
jsRecord["Certificate"] = rr.Certificate
|
||||||
|
jsRecord["MatchingType"] = rr.MatchingType
|
||||||
|
jsRecord["Selector"] = rr.Selector
|
||||||
|
jsRecord["Usage"] = rr.Usage
|
||||||
|
case *dns.CERT:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["Certificate"] = rr.Certificate
|
||||||
|
jsRecord["KeyTag"] = rr.KeyTag
|
||||||
|
jsRecord["Type"] = rr.Type
|
||||||
|
case *dns.DS:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["Digest"] = rr.Digest
|
||||||
|
jsRecord["DigestType"] = rr.DigestType
|
||||||
|
jsRecord["KeyTag"] = rr.KeyTag
|
||||||
|
case *dns.NAPTR:
|
||||||
|
jsRecord["Order"] = rr.Order
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
jsRecord["Flags"] = rr.Flags
|
||||||
|
jsRecord["Service"] = rr.Service
|
||||||
|
jsRecord["Regexp"] = rr.Regexp
|
||||||
|
jsRecord["Replacement"] = rr.Replacement
|
||||||
|
case *dns.RRSIG:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["Expiration"] = rr.Expiration
|
||||||
|
jsRecord["Inception"] = rr.Inception
|
||||||
|
jsRecord["KeyTag"] = rr.KeyTag
|
||||||
|
jsRecord["Labels"] = rr.Labels
|
||||||
|
jsRecord["OrigTtl"] = rr.OrigTtl
|
||||||
|
jsRecord["Signature"] = rr.Signature
|
||||||
|
jsRecord["SignerName"] = rr.SignerName
|
||||||
|
jsRecord["TypeCovered"] = rr.TypeCovered
|
||||||
|
case *dns.NSEC:
|
||||||
|
jsRecord["NextDomain"] = rr.NextDomain
|
||||||
|
jsRecord["TypeBitMap"] = rr.TypeBitMap
|
||||||
|
case *dns.NSEC3:
|
||||||
|
jsRecord["Flags"] = rr.Flags
|
||||||
|
jsRecord["Hash"] = rr.Hash
|
||||||
|
jsRecord["HashLength"] = rr.HashLength
|
||||||
|
jsRecord["Iterations"] = rr.Iterations
|
||||||
|
jsRecord["NextDomain"] = rr.NextDomain
|
||||||
|
jsRecord["Salt"] = rr.Salt
|
||||||
|
jsRecord["SaltLength"] = rr.SaltLength
|
||||||
|
jsRecord["TypeBitMap"] = rr.TypeBitMap
|
||||||
|
case *dns.NSEC3PARAM:
|
||||||
|
jsRecord["Flags"] = rr.Flags
|
||||||
|
jsRecord["Hash"] = rr.Hash
|
||||||
|
jsRecord["Iterations"] = rr.Iterations
|
||||||
|
jsRecord["Salt"] = rr.Salt
|
||||||
|
jsRecord["SaltLength"] = rr.SaltLength
|
||||||
|
case *dns.TKEY:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["Error"] = rr.Error
|
||||||
|
jsRecord["Expiration"] = rr.Expiration
|
||||||
|
jsRecord["Inception"] = rr.Inception
|
||||||
|
jsRecord["Key"] = rr.Key
|
||||||
|
jsRecord["KeySize"] = rr.KeySize
|
||||||
|
jsRecord["Mode"] = rr.Mode
|
||||||
|
jsRecord["OtherData"] = rr.OtherData
|
||||||
|
jsRecord["OtherLen"] = rr.OtherLen
|
||||||
|
case *dns.TSIG:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["Error"] = rr.Error
|
||||||
|
jsRecord["Fudge"] = rr.Fudge
|
||||||
|
jsRecord["MACSize"] = rr.MACSize
|
||||||
|
jsRecord["MAC"] = rr.MAC
|
||||||
|
jsRecord["OrigId"] = rr.OrigId
|
||||||
|
jsRecord["OtherData"] = rr.OtherData
|
||||||
|
jsRecord["OtherLen"] = rr.OtherLen
|
||||||
|
jsRecord["TimeSigned"] = rr.TimeSigned
|
||||||
|
case *dns.IPSECKEY:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["GatewayAddr"] = rr.GatewayAddr.String()
|
||||||
|
jsRecord["GatewayHost"] = rr.GatewayHost
|
||||||
|
jsRecord["GatewayType"] = rr.GatewayType
|
||||||
|
jsRecord["Precedence"] = rr.Precedence
|
||||||
|
jsRecord["PublicKey"] = rr.PublicKey
|
||||||
|
case *dns.KEY:
|
||||||
|
jsRecord["Flags"] = rr.Flags
|
||||||
|
jsRecord["Protocol"] = rr.Protocol
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["PublicKey"] = rr.PublicKey
|
||||||
|
case *dns.CDS:
|
||||||
|
jsRecord["KeyTag"] = rr.KeyTag
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["DigestType"] = rr.DigestType
|
||||||
|
jsRecord["Digest"] = rr.Digest
|
||||||
|
case *dns.CDNSKEY:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["Flags"] = rr.Flags
|
||||||
|
jsRecord["Protocol"] = rr.Protocol
|
||||||
|
jsRecord["PublicKey"] = rr.PublicKey
|
||||||
|
case *dns.NID:
|
||||||
|
jsRecord["NodeID"] = rr.NodeID
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
case *dns.L32:
|
||||||
|
jsRecord["Locator32"] = rr.Locator32.String()
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
case *dns.L64:
|
||||||
|
jsRecord["Locator64"] = rr.Locator64
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
case *dns.LP:
|
||||||
|
jsRecord["Fqdn"] = rr.Fqdn
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
case *dns.GPOS:
|
||||||
|
jsRecord["Altitude"] = rr.Altitude
|
||||||
|
jsRecord["Latitude"] = rr.Latitude
|
||||||
|
jsRecord["Longitude"] = rr.Longitude
|
||||||
|
case *dns.RP:
|
||||||
|
jsRecord["Mbox"] = rr.Mbox
|
||||||
|
jsRecord["Txt"] = rr.Txt
|
||||||
|
case *dns.RKEY:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["Flags"] = rr.Flags
|
||||||
|
jsRecord["Protocol"] = rr.Protocol
|
||||||
|
jsRecord["PublicKey"] = rr.PublicKey
|
||||||
|
case *dns.SMIMEA:
|
||||||
|
jsRecord["Certificate"] = rr.Certificate
|
||||||
|
jsRecord["MatchingType"] = rr.MatchingType
|
||||||
|
jsRecord["Selector"] = rr.Selector
|
||||||
|
jsRecord["Usage"] = rr.Usage
|
||||||
|
case *dns.AMTRELAY:
|
||||||
|
jsRecord["GatewayAddr"] = rr.GatewayAddr.String()
|
||||||
|
jsRecord["GatewayHost"] = rr.GatewayHost
|
||||||
|
jsRecord["GatewayType"] = rr.GatewayType
|
||||||
|
jsRecord["Precedence"] = rr.Precedence
|
||||||
|
case *dns.AVC:
|
||||||
|
jsRecord["Txt"] = rr.Txt
|
||||||
|
case *dns.URI:
|
||||||
|
jsRecord["Priority"] = rr.Priority
|
||||||
|
jsRecord["Weight"] = rr.Weight
|
||||||
|
jsRecord["Target"] = rr.Target
|
||||||
|
case *dns.EUI48:
|
||||||
|
jsRecord["Address"] = rr.Address
|
||||||
|
case *dns.EUI64:
|
||||||
|
jsRecord["Address"] = rr.Address
|
||||||
|
case *dns.GID:
|
||||||
|
jsRecord["Gid"] = rr.Gid
|
||||||
|
case *dns.UID:
|
||||||
|
jsRecord["Uid"] = rr.Uid
|
||||||
|
case *dns.UINFO:
|
||||||
|
jsRecord["Uinfo"] = rr.Uinfo
|
||||||
|
case *dns.SPF:
|
||||||
|
jsRecord["Txt"] = rr.Txt
|
||||||
|
case *dns.HTTPS:
|
||||||
|
jsRecord["Priority"] = rr.Priority
|
||||||
|
jsRecord["Target"] = rr.Target
|
||||||
|
kvs := rr.Value
|
||||||
|
var jsKvs []map[string]interface{}
|
||||||
|
for _, kv := range kvs {
|
||||||
|
jsKv, err := NewJSSVCBKeyValue(kv)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
jsKvs = append(jsKvs, jsKv)
|
||||||
|
}
|
||||||
|
jsRecord["Value"] = jsKvs
|
||||||
|
case *dns.SVCB:
|
||||||
|
jsRecord["Priority"] = rr.Priority
|
||||||
|
jsRecord["Target"] = rr.Target
|
||||||
|
kvs := rr.Value
|
||||||
|
jsKvs := make([]map[string]interface{}, len(kvs))
|
||||||
|
for i, kv := range kvs {
|
||||||
|
jsKv, err := NewJSSVCBKeyValue(kv)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
jsKvs[i] = jsKv
|
||||||
|
}
|
||||||
|
jsRecord["Value"] = jsKvs
|
||||||
|
case *dns.ZONEMD:
|
||||||
|
jsRecord["Digest"] = rr.Digest
|
||||||
|
jsRecord["Hash"] = rr.Hash
|
||||||
|
jsRecord["Scheme"] = rr.Scheme
|
||||||
|
jsRecord["Serial"] = rr.Serial
|
||||||
|
case *dns.CSYNC:
|
||||||
|
jsRecord["Flags"] = rr.Flags
|
||||||
|
jsRecord["Serial"] = rr.Serial
|
||||||
|
jsRecord["TypeBitMap"] = rr.TypeBitMap
|
||||||
|
case *dns.OPENPGPKEY:
|
||||||
|
jsRecord["PublicKey"] = rr.PublicKey
|
||||||
|
case *dns.TALINK:
|
||||||
|
jsRecord["NextName"] = rr.NextName
|
||||||
|
jsRecord["PreviousName"] = rr.PreviousName
|
||||||
|
case *dns.NINFO:
|
||||||
|
jsRecord["ZSData"] = rr.ZSData
|
||||||
|
case *dns.DHCID:
|
||||||
|
jsRecord["Digest"] = rr.Digest
|
||||||
|
case *dns.DNSKEY:
|
||||||
|
jsRecord["Flags"] = rr.Flags
|
||||||
|
jsRecord["Protocol"] = rr.Protocol
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["PublicKey"] = rr.PublicKey
|
||||||
|
case *dns.HIP:
|
||||||
|
jsRecord["Hit"] = rr.Hit
|
||||||
|
jsRecord["HitLength"] = rr.HitLength
|
||||||
|
jsRecord["PublicKey"] = rr.PublicKey
|
||||||
|
jsRecord["PublicKeyAlgorithm"] = rr.PublicKeyAlgorithm
|
||||||
|
jsRecord["PublicKeyLength"] = rr.PublicKeyLength
|
||||||
|
jsRecord["RendezvousServers"] = rr.RendezvousServers
|
||||||
|
case *dns.OPT:
|
||||||
|
jsRecord["Option"] = rr.Option
|
||||||
|
case *dns.NIMLOC:
|
||||||
|
jsRecord["Locator"] = rr.Locator
|
||||||
|
case *dns.EID:
|
||||||
|
jsRecord["Endpoint"] = rr.Endpoint
|
||||||
|
case *dns.NXT:
|
||||||
|
jsRecord["NextDomain"] = rr.NextDomain
|
||||||
|
jsRecord["TypeBitMap"] = rr.TypeBitMap
|
||||||
|
case *dns.PX:
|
||||||
|
jsRecord["Mapx400"] = rr.Mapx400
|
||||||
|
jsRecord["Map822"] = rr.Map822
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
case *dns.SIG:
|
||||||
|
jsRecord["Algorithm"] = rr.Algorithm
|
||||||
|
jsRecord["Expiration"] = rr.Expiration
|
||||||
|
jsRecord["Inception"] = rr.Inception
|
||||||
|
jsRecord["KeyTag"] = rr.KeyTag
|
||||||
|
jsRecord["Labels"] = rr.Labels
|
||||||
|
jsRecord["OrigTtl"] = rr.OrigTtl
|
||||||
|
jsRecord["Signature"] = rr.Signature
|
||||||
|
jsRecord["SignerName"] = rr.SignerName
|
||||||
|
jsRecord["TypeCovered"] = rr.TypeCovered
|
||||||
|
case *dns.RT:
|
||||||
|
jsRecord["Host"] = rr.Host
|
||||||
|
jsRecord["Preference"] = rr.Preference
|
||||||
|
case *dns.NSAPPTR:
|
||||||
|
jsRecord["Ptr"] = rr.Ptr
|
||||||
|
case *dns.X25:
|
||||||
|
jsRecord["PSDNAddress"] = rr.PSDNAddress
|
||||||
|
case *dns.RFC3597:
|
||||||
|
jsRecord["Rdata"] = rr.Rdata
|
||||||
|
// case *dns.ATMA:
|
||||||
|
// case *dns.WKS:
|
||||||
|
// case *dns.DOA:
|
||||||
|
// case *dns.SINK:
|
||||||
|
default:
|
||||||
|
if header.Rrtype == dns.TypeNone {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("error creating JSResourceRecord: unknown type: %d", header.Rrtype)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsRecord, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToRR(jsRecord map[string]interface{}) (rr dns.RR, err error) {
|
||||||
|
jsHeader := jsPropToMap(jsRecord, "Header")
|
||||||
|
|
||||||
|
header := dns.RR_Header{
|
||||||
|
Class: jsPropToUint16(jsHeader, "Class"),
|
||||||
|
Name: jsPropToString(jsHeader, "Name"),
|
||||||
|
Rrtype: jsPropToUint16(jsHeader, "Rrtype"),
|
||||||
|
Ttl: jsPropToUint32(jsHeader, "Ttl"),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch header.Rrtype {
|
||||||
|
case dns.TypeNone:
|
||||||
|
break
|
||||||
|
case dns.TypeA:
|
||||||
|
rr = &dns.A{
|
||||||
|
Hdr: header,
|
||||||
|
A: net.ParseIP(jsPropToString(jsRecord, "A")),
|
||||||
|
}
|
||||||
|
case dns.TypeAAAA:
|
||||||
|
rr = &dns.AAAA{
|
||||||
|
Hdr: header,
|
||||||
|
AAAA: net.ParseIP(jsPropToString(jsRecord, "AAAA")),
|
||||||
|
}
|
||||||
|
case dns.TypeAPL:
|
||||||
|
jsPrefixes := jsRecord["Prefixes"].([]map[string]interface{})
|
||||||
|
prefixes := make([]dns.APLPrefix, len(jsPrefixes))
|
||||||
|
for i, jsPrefix := range jsPrefixes {
|
||||||
|
jsNetwork := jsPrefix["Network"].(string)
|
||||||
|
_, network, err := net.ParseCIDR(jsNetwork)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("error parsing CIDR: %s", jsNetwork)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
prefixes[i] = dns.APLPrefix{
|
||||||
|
Negation: jsPrefix["Negation"].(bool),
|
||||||
|
Network: *network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rr = &dns.APL{
|
||||||
|
Hdr: header,
|
||||||
|
Prefixes: prefixes,
|
||||||
|
}
|
||||||
|
case dns.TypeCNAME:
|
||||||
|
rr = &dns.CNAME{
|
||||||
|
Hdr: header,
|
||||||
|
Target: jsPropToString(jsRecord, "Target"),
|
||||||
|
}
|
||||||
|
case dns.TypeMB:
|
||||||
|
rr = &dns.MB{
|
||||||
|
Hdr: header,
|
||||||
|
Mb: jsPropToString(jsRecord, "Mb"),
|
||||||
|
}
|
||||||
|
case dns.TypeMD:
|
||||||
|
rr = &dns.MD{
|
||||||
|
Hdr: header,
|
||||||
|
Md: jsPropToString(jsRecord, "Md"),
|
||||||
|
}
|
||||||
|
case dns.TypeMF:
|
||||||
|
rr = &dns.MF{
|
||||||
|
Hdr: header,
|
||||||
|
Mf: jsPropToString(jsRecord, "Mf"),
|
||||||
|
}
|
||||||
|
case dns.TypeMG:
|
||||||
|
rr = &dns.MG{
|
||||||
|
Hdr: header,
|
||||||
|
Mg: jsPropToString(jsRecord, "Mg"),
|
||||||
|
}
|
||||||
|
case dns.TypeMR:
|
||||||
|
rr = &dns.MR{
|
||||||
|
Hdr: header,
|
||||||
|
Mr: jsPropToString(jsRecord, "Mr"),
|
||||||
|
}
|
||||||
|
case dns.TypeMX:
|
||||||
|
rr = &dns.MX{
|
||||||
|
Hdr: header,
|
||||||
|
Mx: jsPropToString(jsRecord, "Mx"),
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
}
|
||||||
|
case dns.TypeNULL:
|
||||||
|
rr = &dns.NULL{
|
||||||
|
Hdr: header,
|
||||||
|
Data: jsPropToString(jsRecord, "Data"),
|
||||||
|
}
|
||||||
|
case dns.TypeSOA:
|
||||||
|
rr = &dns.SOA{
|
||||||
|
Hdr: header,
|
||||||
|
Expire: jsPropToUint32(jsRecord, "Expire"),
|
||||||
|
Mbox: jsPropToString(jsRecord, "Mbox"),
|
||||||
|
Minttl: jsPropToUint32(jsRecord, "Minttl"),
|
||||||
|
Ns: jsPropToString(jsRecord, "Ns"),
|
||||||
|
Refresh: jsPropToUint32(jsRecord, "Refresh"),
|
||||||
|
Retry: jsPropToUint32(jsRecord, "Retry"),
|
||||||
|
Serial: jsPropToUint32(jsRecord, "Serial"),
|
||||||
|
}
|
||||||
|
case dns.TypeTXT:
|
||||||
|
rr = &dns.TXT{
|
||||||
|
Hdr: header,
|
||||||
|
Txt: jsPropToStringArray(jsRecord, "Txt"),
|
||||||
|
}
|
||||||
|
case dns.TypeSRV:
|
||||||
|
rr = &dns.SRV{
|
||||||
|
Hdr: header,
|
||||||
|
Port: jsPropToUint16(jsRecord, "Port"),
|
||||||
|
Priority: jsPropToUint16(jsRecord, "Priority"),
|
||||||
|
Target: jsPropToString(jsRecord, "Target"),
|
||||||
|
Weight: jsPropToUint16(jsRecord, "Weight"),
|
||||||
|
}
|
||||||
|
case dns.TypePTR:
|
||||||
|
rr = &dns.PTR{
|
||||||
|
Hdr: header,
|
||||||
|
Ptr: jsPropToString(jsRecord, "Ptr"),
|
||||||
|
}
|
||||||
|
case dns.TypeNS:
|
||||||
|
rr = &dns.NS{
|
||||||
|
Hdr: header,
|
||||||
|
Ns: jsPropToString(jsRecord, "Ns"),
|
||||||
|
}
|
||||||
|
case dns.TypeDNAME:
|
||||||
|
rr = &dns.DNAME{
|
||||||
|
Hdr: header,
|
||||||
|
Target: jsPropToString(jsRecord, "Target"),
|
||||||
|
}
|
||||||
|
case dns.TypeAFSDB:
|
||||||
|
rr = &dns.AFSDB{
|
||||||
|
Hdr: header,
|
||||||
|
Hostname: jsPropToString(jsRecord, "Hostname"),
|
||||||
|
Subtype: jsPropToUint16(jsRecord, "Subtype"),
|
||||||
|
}
|
||||||
|
case dns.TypeCAA:
|
||||||
|
rr = &dns.CAA{
|
||||||
|
Hdr: header,
|
||||||
|
Flag: jsPropToUint8(jsRecord, "Flag"),
|
||||||
|
Tag: jsPropToString(jsRecord, "Tag"),
|
||||||
|
Value: jsPropToString(jsRecord, "Value"),
|
||||||
|
}
|
||||||
|
case dns.TypeHINFO:
|
||||||
|
rr = &dns.HINFO{
|
||||||
|
Hdr: header,
|
||||||
|
Cpu: jsPropToString(jsRecord, "Cpu"),
|
||||||
|
Os: jsPropToString(jsRecord, "Os"),
|
||||||
|
}
|
||||||
|
case dns.TypeMINFO:
|
||||||
|
rr = &dns.MINFO{
|
||||||
|
Hdr: header,
|
||||||
|
Email: jsPropToString(jsRecord, "Email"),
|
||||||
|
Rmail: jsPropToString(jsRecord, "Rmail"),
|
||||||
|
}
|
||||||
|
case dns.TypeISDN:
|
||||||
|
rr = &dns.ISDN{
|
||||||
|
Hdr: header,
|
||||||
|
Address: jsPropToString(jsRecord, "Address"),
|
||||||
|
SubAddress: jsPropToString(jsRecord, "SubAddress"),
|
||||||
|
}
|
||||||
|
case dns.TypeKX:
|
||||||
|
rr = &dns.KX{
|
||||||
|
Hdr: header,
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
Exchanger: jsPropToString(jsRecord, "Exchanger"),
|
||||||
|
}
|
||||||
|
case dns.TypeLOC:
|
||||||
|
rr = &dns.LOC{
|
||||||
|
Hdr: header,
|
||||||
|
Version: jsPropToUint8(jsRecord, "Version"),
|
||||||
|
Size: jsPropToUint8(jsRecord, "Size"),
|
||||||
|
HorizPre: jsPropToUint8(jsRecord, "HorizPre"),
|
||||||
|
VertPre: jsPropToUint8(jsRecord, "VertPre"),
|
||||||
|
Latitude: jsPropToUint32(jsRecord, "Latitude"),
|
||||||
|
Longitude: jsPropToUint32(jsRecord, "Longitude"),
|
||||||
|
Altitude: jsPropToUint32(jsRecord, "Altitude"),
|
||||||
|
}
|
||||||
|
case dns.TypeSSHFP:
|
||||||
|
rr = &dns.SSHFP{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
FingerPrint: jsPropToString(jsRecord, "FingerPrint"),
|
||||||
|
Type: jsPropToUint8(jsRecord, "Type"),
|
||||||
|
}
|
||||||
|
case dns.TypeTLSA:
|
||||||
|
rr = &dns.TLSA{
|
||||||
|
Hdr: header,
|
||||||
|
Certificate: jsPropToString(jsRecord, "Certificate"),
|
||||||
|
MatchingType: jsPropToUint8(jsRecord, "MatchingType"),
|
||||||
|
Selector: jsPropToUint8(jsRecord, "Selector"),
|
||||||
|
Usage: jsPropToUint8(jsRecord, "Usage"),
|
||||||
|
}
|
||||||
|
case dns.TypeCERT:
|
||||||
|
rr = &dns.CERT{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
Certificate: jsPropToString(jsRecord, "Certificate"),
|
||||||
|
KeyTag: jsPropToUint16(jsRecord, "KeyTag"),
|
||||||
|
Type: jsPropToUint16(jsRecord, "Type"),
|
||||||
|
}
|
||||||
|
case dns.TypeDS:
|
||||||
|
rr = &dns.DS{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
Digest: jsPropToString(jsRecord, "Digest"),
|
||||||
|
DigestType: jsPropToUint8(jsRecord, "DigestType"),
|
||||||
|
KeyTag: jsPropToUint16(jsRecord, "KeyTag"),
|
||||||
|
}
|
||||||
|
case dns.TypeNAPTR:
|
||||||
|
rr = &dns.NAPTR{
|
||||||
|
Hdr: header,
|
||||||
|
Flags: jsPropToString(jsRecord, "Flags"),
|
||||||
|
Order: jsPropToUint16(jsRecord, "Order"),
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
Regexp: jsPropToString(jsRecord, "Regexp"),
|
||||||
|
Replacement: jsPropToString(jsRecord, "Replacement"),
|
||||||
|
Service: jsPropToString(jsRecord, "Service"),
|
||||||
|
}
|
||||||
|
case dns.TypeRRSIG:
|
||||||
|
rr = &dns.RRSIG{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
Expiration: jsPropToUint32(jsRecord, "Expiration"),
|
||||||
|
Inception: jsPropToUint32(jsRecord, "Inception"),
|
||||||
|
KeyTag: jsPropToUint16(jsRecord, "KeyTag"),
|
||||||
|
Labels: jsPropToUint8(jsRecord, "Labels"),
|
||||||
|
OrigTtl: jsPropToUint32(jsRecord, "OrigTtl"),
|
||||||
|
Signature: jsPropToString(jsRecord, "Signature"),
|
||||||
|
SignerName: jsPropToString(jsRecord, "SignerName"),
|
||||||
|
TypeCovered: jsPropToUint16(jsRecord, "TypeCovered"),
|
||||||
|
}
|
||||||
|
case dns.TypeNSEC:
|
||||||
|
rr = &dns.NSEC{
|
||||||
|
Hdr: header,
|
||||||
|
NextDomain: jsPropToString(jsRecord, "NextDomain"),
|
||||||
|
TypeBitMap: jsPropToUint16Array(jsRecord, "TypeBitMap"),
|
||||||
|
}
|
||||||
|
case dns.TypeNSEC3:
|
||||||
|
rr = &dns.NSEC3{
|
||||||
|
Hdr: header,
|
||||||
|
Flags: jsPropToUint8(jsRecord, "Flags"),
|
||||||
|
Hash: jsPropToUint8(jsRecord, "Hash"),
|
||||||
|
HashLength: jsPropToUint8(jsRecord, "HashLength"),
|
||||||
|
Iterations: jsPropToUint16(jsRecord, "Iterations"),
|
||||||
|
NextDomain: jsPropToString(jsRecord, "NextDomain"),
|
||||||
|
Salt: jsPropToString(jsRecord, "Salt"),
|
||||||
|
SaltLength: jsPropToUint8(jsRecord, "SaltLength"),
|
||||||
|
TypeBitMap: jsPropToUint16Array(jsRecord, "TypeBitMap"),
|
||||||
|
}
|
||||||
|
case dns.TypeNSEC3PARAM:
|
||||||
|
rr = &dns.NSEC3PARAM{
|
||||||
|
Hdr: header,
|
||||||
|
Flags: jsPropToUint8(jsRecord, "Flags"),
|
||||||
|
Hash: jsPropToUint8(jsRecord, "Hash"),
|
||||||
|
Iterations: jsPropToUint16(jsRecord, "Iterations"),
|
||||||
|
Salt: jsPropToString(jsRecord, "Salt"),
|
||||||
|
SaltLength: jsPropToUint8(jsRecord, "SaltLength"),
|
||||||
|
}
|
||||||
|
case dns.TypeTKEY:
|
||||||
|
rr = &dns.TKEY{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToString(jsRecord, "Algorithm"),
|
||||||
|
Error: jsPropToUint16(jsRecord, "Error"),
|
||||||
|
Expiration: jsPropToUint32(jsRecord, "Expiration"),
|
||||||
|
Inception: jsPropToUint32(jsRecord, "Inception"),
|
||||||
|
Key: jsPropToString(jsRecord, "Key"),
|
||||||
|
KeySize: jsPropToUint16(jsRecord, "KeySize"),
|
||||||
|
Mode: jsPropToUint16(jsRecord, "Mode"),
|
||||||
|
OtherData: jsPropToString(jsRecord, "OtherData"),
|
||||||
|
OtherLen: jsPropToUint16(jsRecord, "OtherLen"),
|
||||||
|
}
|
||||||
|
case dns.TypeTSIG:
|
||||||
|
rr = &dns.TSIG{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToString(jsRecord, "Algorithm"),
|
||||||
|
Error: jsPropToUint16(jsRecord, "Error"),
|
||||||
|
Fudge: jsPropToUint16(jsRecord, "Fudge"),
|
||||||
|
MACSize: jsPropToUint16(jsRecord, "MACSize"),
|
||||||
|
MAC: jsPropToString(jsRecord, "MAC"),
|
||||||
|
OrigId: jsPropToUint16(jsRecord, "OrigId"),
|
||||||
|
OtherData: jsPropToString(jsRecord, "OtherData"),
|
||||||
|
OtherLen: jsPropToUint16(jsRecord, "OtherLen"),
|
||||||
|
TimeSigned: jsPropToUint64(jsRecord, "TimeSigned"),
|
||||||
|
}
|
||||||
|
case dns.TypeIPSECKEY:
|
||||||
|
rr = &dns.IPSECKEY{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
GatewayAddr: net.IP(jsPropToString(jsRecord, "GatewayAddr")),
|
||||||
|
GatewayHost: jsPropToString(jsRecord, "GatewayHost"),
|
||||||
|
GatewayType: jsPropToUint8(jsRecord, "GatewayType"),
|
||||||
|
Precedence: jsPropToUint8(jsRecord, "Precedence"),
|
||||||
|
PublicKey: jsPropToString(jsRecord, "PublicKey"),
|
||||||
|
}
|
||||||
|
case dns.TypeKEY:
|
||||||
|
rr = &dns.KEY{
|
||||||
|
DNSKEY: dns.DNSKEY{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
Flags: jsPropToUint16(jsRecord, "Flags"),
|
||||||
|
Protocol: jsPropToUint8(jsRecord, "Protocol"),
|
||||||
|
PublicKey: jsPropToString(jsRecord, "PublicKey"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case dns.TypeCDS:
|
||||||
|
rr = &dns.CDS{
|
||||||
|
DS: dns.DS{
|
||||||
|
Hdr: header,
|
||||||
|
KeyTag: jsPropToUint16(jsRecord, "KeyTag"),
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
DigestType: jsPropToUint8(jsRecord, "DigestType"),
|
||||||
|
Digest: jsPropToString(jsRecord, "Digest"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case dns.TypeCDNSKEY:
|
||||||
|
rr = &dns.CDNSKEY{
|
||||||
|
DNSKEY: dns.DNSKEY{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
Flags: jsPropToUint16(jsRecord, "Flags"),
|
||||||
|
Protocol: jsPropToUint8(jsRecord, "Protocol"),
|
||||||
|
PublicKey: jsPropToString(jsRecord, "PublicKey"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case dns.TypeNID:
|
||||||
|
rr = &dns.NID{
|
||||||
|
Hdr: header,
|
||||||
|
NodeID: jsPropToUint64(jsRecord, "NodeID"),
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
}
|
||||||
|
case dns.TypeL32:
|
||||||
|
rr = &dns.L32{
|
||||||
|
Hdr: header,
|
||||||
|
Locator32: net.IP(jsPropToString(jsRecord, "Locator32")),
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
}
|
||||||
|
case dns.TypeL64:
|
||||||
|
rr = &dns.L64{
|
||||||
|
Hdr: header,
|
||||||
|
Locator64: jsPropToUint64(jsRecord, "Locator64"),
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
}
|
||||||
|
case dns.TypeLP:
|
||||||
|
rr = &dns.LP{
|
||||||
|
Hdr: header,
|
||||||
|
Fqdn: jsPropToString(jsRecord, "Fqdn"),
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
}
|
||||||
|
case dns.TypeGPOS:
|
||||||
|
rr = &dns.GPOS{
|
||||||
|
Hdr: header,
|
||||||
|
Altitude: jsPropToString(jsRecord, "Altitude"),
|
||||||
|
Latitude: jsPropToString(jsRecord, "Latitude"),
|
||||||
|
Longitude: jsPropToString(jsRecord, "Longitude"),
|
||||||
|
}
|
||||||
|
case dns.TypeRP:
|
||||||
|
rr = &dns.RP{
|
||||||
|
Hdr: header,
|
||||||
|
Mbox: jsPropToString(jsRecord, "Mbox"),
|
||||||
|
Txt: jsPropToString(jsRecord, "Txt"),
|
||||||
|
}
|
||||||
|
case dns.TypeRKEY:
|
||||||
|
rr = &dns.RKEY{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
Flags: jsPropToUint16(jsRecord, "Flags"),
|
||||||
|
Protocol: jsPropToUint8(jsRecord, "Protocol"),
|
||||||
|
PublicKey: jsPropToString(jsRecord, "PublicKey"),
|
||||||
|
}
|
||||||
|
case dns.TypeSMIMEA:
|
||||||
|
rr = &dns.SMIMEA{
|
||||||
|
Hdr: header,
|
||||||
|
Certificate: jsPropToString(jsRecord, "Certificate"),
|
||||||
|
MatchingType: jsPropToUint8(jsRecord, "MatchingType"),
|
||||||
|
Selector: jsPropToUint8(jsRecord, "Selector"),
|
||||||
|
Usage: jsPropToUint8(jsRecord, "Usage"),
|
||||||
|
}
|
||||||
|
case dns.TypeAMTRELAY:
|
||||||
|
rr = &dns.AMTRELAY{
|
||||||
|
Hdr: header,
|
||||||
|
GatewayAddr: net.IP(jsPropToString(jsRecord, "GatewayAddr")),
|
||||||
|
GatewayHost: jsPropToString(jsRecord, "GatewayHost"),
|
||||||
|
GatewayType: jsPropToUint8(jsRecord, "GatewayType"),
|
||||||
|
Precedence: jsPropToUint8(jsRecord, "Precedence"),
|
||||||
|
}
|
||||||
|
case dns.TypeAVC:
|
||||||
|
rr = &dns.AVC{
|
||||||
|
Hdr: header,
|
||||||
|
Txt: jsPropToStringArray(jsRecord, "Txt"),
|
||||||
|
}
|
||||||
|
case dns.TypeURI:
|
||||||
|
rr = &dns.URI{
|
||||||
|
Hdr: header,
|
||||||
|
Priority: jsPropToUint16(jsRecord, "Priority"),
|
||||||
|
Weight: jsPropToUint16(jsRecord, "Weight"),
|
||||||
|
Target: jsPropToString(jsRecord, "Target"),
|
||||||
|
}
|
||||||
|
case dns.TypeEUI48:
|
||||||
|
rr = &dns.EUI48{
|
||||||
|
Hdr: header,
|
||||||
|
Address: jsPropToUint64(jsRecord, "Address"),
|
||||||
|
}
|
||||||
|
case dns.TypeEUI64:
|
||||||
|
rr = &dns.EUI64{
|
||||||
|
Hdr: header,
|
||||||
|
Address: jsPropToUint64(jsRecord, "Address"),
|
||||||
|
}
|
||||||
|
case dns.TypeGID:
|
||||||
|
rr = &dns.GID{
|
||||||
|
Hdr: header,
|
||||||
|
Gid: jsPropToUint32(jsRecord, "Gid"),
|
||||||
|
}
|
||||||
|
case dns.TypeUID:
|
||||||
|
rr = &dns.UID{
|
||||||
|
Hdr: header,
|
||||||
|
Uid: jsPropToUint32(jsRecord, "Uid"),
|
||||||
|
}
|
||||||
|
case dns.TypeUINFO:
|
||||||
|
rr = &dns.UINFO{
|
||||||
|
Hdr: header,
|
||||||
|
Uinfo: jsPropToString(jsRecord, "Uinfo"),
|
||||||
|
}
|
||||||
|
case dns.TypeSPF:
|
||||||
|
rr = &dns.SPF{
|
||||||
|
Hdr: header,
|
||||||
|
Txt: jsPropToStringArray(jsRecord, "Txt"),
|
||||||
|
}
|
||||||
|
case dns.TypeHTTPS:
|
||||||
|
jsKvs := jsPropToMapArray(jsRecord, "Value")
|
||||||
|
var kvs []dns.SVCBKeyValue
|
||||||
|
for _, jsKv := range jsKvs {
|
||||||
|
kv, err := ToSVCBKeyValue(jsKv)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kvs = append(kvs, kv)
|
||||||
|
}
|
||||||
|
rr = &dns.HTTPS{
|
||||||
|
SVCB: dns.SVCB{
|
||||||
|
Hdr: header,
|
||||||
|
Priority: jsPropToUint16(jsRecord, "Priority"),
|
||||||
|
Target: jsPropToString(jsRecord, "Target"),
|
||||||
|
Value: kvs,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case dns.TypeSVCB:
|
||||||
|
jsKvs := jsPropToMapArray(jsRecord, "Value")
|
||||||
|
var kvs []dns.SVCBKeyValue
|
||||||
|
for _, jsKv := range jsKvs {
|
||||||
|
kv, err := ToSVCBKeyValue(jsKv)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kvs = append(kvs, kv)
|
||||||
|
}
|
||||||
|
rr = &dns.SVCB{
|
||||||
|
Hdr: header,
|
||||||
|
Priority: jsPropToUint16(jsRecord, "Priority"),
|
||||||
|
Target: jsPropToString(jsRecord, "Target"),
|
||||||
|
Value: kvs,
|
||||||
|
}
|
||||||
|
case dns.TypeZONEMD:
|
||||||
|
rr = &dns.ZONEMD{
|
||||||
|
Hdr: header,
|
||||||
|
Digest: jsPropToString(jsRecord, "Digest"),
|
||||||
|
Hash: jsPropToUint8(jsRecord, "Hash"),
|
||||||
|
Scheme: jsPropToUint8(jsRecord, "Scheme"),
|
||||||
|
Serial: jsPropToUint32(jsRecord, "Serial"),
|
||||||
|
}
|
||||||
|
case dns.TypeCSYNC:
|
||||||
|
rr = &dns.CSYNC{
|
||||||
|
Hdr: header,
|
||||||
|
Flags: jsPropToUint16(jsRecord, "Flags"),
|
||||||
|
Serial: jsPropToUint32(jsRecord, "Serial"),
|
||||||
|
TypeBitMap: jsPropToUint16Array(jsRecord, "TypeBitMap"),
|
||||||
|
}
|
||||||
|
case dns.TypeOPENPGPKEY:
|
||||||
|
rr = &dns.OPENPGPKEY{
|
||||||
|
Hdr: header,
|
||||||
|
PublicKey: jsPropToString(jsRecord, "PublicKey"),
|
||||||
|
}
|
||||||
|
case dns.TypeTALINK:
|
||||||
|
rr = &dns.TALINK{
|
||||||
|
Hdr: header,
|
||||||
|
NextName: jsPropToString(jsRecord, "NextName"),
|
||||||
|
PreviousName: jsPropToString(jsRecord, "PreviousName"),
|
||||||
|
}
|
||||||
|
case dns.TypeNINFO:
|
||||||
|
rr = &dns.NINFO{
|
||||||
|
Hdr: header,
|
||||||
|
ZSData: jsPropToStringArray(jsRecord, "ZSData"),
|
||||||
|
}
|
||||||
|
case dns.TypeDHCID:
|
||||||
|
rr = &dns.DHCID{
|
||||||
|
Hdr: header,
|
||||||
|
Digest: jsPropToString(jsRecord, "Digest"),
|
||||||
|
}
|
||||||
|
case dns.TypeDNSKEY:
|
||||||
|
rr = &dns.DNSKEY{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
Flags: jsPropToUint16(jsRecord, "Flags"),
|
||||||
|
Protocol: jsPropToUint8(jsRecord, "Protocol"),
|
||||||
|
PublicKey: jsPropToString(jsRecord, "PublicKey"),
|
||||||
|
}
|
||||||
|
case dns.TypeHIP:
|
||||||
|
rr = &dns.HIP{
|
||||||
|
Hdr: header,
|
||||||
|
Hit: jsPropToString(jsRecord, "Hit"),
|
||||||
|
HitLength: jsPropToUint8(jsRecord, "HitLength"),
|
||||||
|
PublicKey: jsPropToString(jsRecord, "PublicKey"),
|
||||||
|
PublicKeyAlgorithm: jsPropToUint8(jsRecord, "PublicKeyAlgorithm"),
|
||||||
|
PublicKeyLength: jsPropToUint16(jsRecord, "PublicKeyLength"),
|
||||||
|
RendezvousServers: jsPropToStringArray(jsRecord, "RendezvousServers"),
|
||||||
|
}
|
||||||
|
case dns.TypeOPT:
|
||||||
|
jsOptions := jsPropToMapArray(jsRecord, "Option")
|
||||||
|
var options []dns.EDNS0
|
||||||
|
for _, jsOption := range jsOptions {
|
||||||
|
option, err := ToEDNS0(jsOption)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
options = append(options, option)
|
||||||
|
}
|
||||||
|
rr = &dns.OPT{
|
||||||
|
Hdr: header,
|
||||||
|
Option: options,
|
||||||
|
}
|
||||||
|
case dns.TypeNIMLOC:
|
||||||
|
rr = &dns.NIMLOC{
|
||||||
|
Hdr: header,
|
||||||
|
Locator: jsPropToString(jsRecord, "Locator"),
|
||||||
|
}
|
||||||
|
case dns.TypeEID:
|
||||||
|
rr = &dns.EID{
|
||||||
|
Hdr: header,
|
||||||
|
Endpoint: jsPropToString(jsRecord, "Endpoint"),
|
||||||
|
}
|
||||||
|
case dns.TypeNXT:
|
||||||
|
rr = &dns.NXT{
|
||||||
|
NSEC: dns.NSEC{
|
||||||
|
Hdr: header,
|
||||||
|
NextDomain: jsPropToString(jsRecord, "NextDomain"),
|
||||||
|
TypeBitMap: jsPropToUint16Array(jsRecord, "TypeBitMap"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case dns.TypePX:
|
||||||
|
rr = &dns.PX{
|
||||||
|
Hdr: header,
|
||||||
|
Mapx400: jsPropToString(jsRecord, "Mapx400"),
|
||||||
|
Map822: jsPropToString(jsRecord, "Map822"),
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
}
|
||||||
|
case dns.TypeSIG:
|
||||||
|
rr = &dns.SIG{
|
||||||
|
RRSIG: dns.RRSIG{
|
||||||
|
Hdr: header,
|
||||||
|
Algorithm: jsPropToUint8(jsRecord, "Algorithm"),
|
||||||
|
Expiration: jsPropToUint32(jsRecord, "Expiration"),
|
||||||
|
Inception: jsPropToUint32(jsRecord, "Inception"),
|
||||||
|
KeyTag: jsPropToUint16(jsRecord, "KeyTag"),
|
||||||
|
Labels: jsPropToUint8(jsRecord, "Labels"),
|
||||||
|
OrigTtl: jsPropToUint32(jsRecord, "OrigTtl"),
|
||||||
|
Signature: jsPropToString(jsRecord, "Signature"),
|
||||||
|
SignerName: jsPropToString(jsRecord, "SignerName"),
|
||||||
|
TypeCovered: jsPropToUint16(jsRecord, "TypeCovered"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case dns.TypeRT:
|
||||||
|
rr = &dns.RT{
|
||||||
|
Hdr: header,
|
||||||
|
Host: jsPropToString(jsRecord, "Host"),
|
||||||
|
Preference: jsPropToUint16(jsRecord, "Preference"),
|
||||||
|
}
|
||||||
|
case dns.TypeNSAPPTR:
|
||||||
|
rr = &dns.NSAPPTR{
|
||||||
|
Hdr: header,
|
||||||
|
Ptr: jsPropToString(jsRecord, "Ptr"),
|
||||||
|
}
|
||||||
|
case dns.TypeX25:
|
||||||
|
rr = &dns.X25{
|
||||||
|
Hdr: header,
|
||||||
|
PSDNAddress: jsPropToString(jsRecord, "PSDNAddress"),
|
||||||
|
}
|
||||||
|
// case dns.TypeATMA:
|
||||||
|
// case dns.TypeWKS:
|
||||||
|
// case dns.TypeDOA:
|
||||||
|
// case dns.TypeSINK:
|
||||||
|
default:
|
||||||
|
if rdata, ok := jsRecord["Rdata"].(string); ok {
|
||||||
|
rr = &dns.RFC3597{
|
||||||
|
Hdr: header,
|
||||||
|
Rdata: rdata,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("error converting to dns.RR: unknown type: %d", header.Rrtype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rr, nil
|
||||||
|
}
|
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
|
||||||
|
}
|
126
modules/dns_proxy/dns_proxy_script.go
Normal file
126
modules/dns_proxy/dns_proxy_script.go
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package dns_proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bettercap/bettercap/v2/log"
|
||||||
|
"github.com/bettercap/bettercap/v2/session"
|
||||||
|
"github.com/evilsocket/islazy/plugin"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DnsProxyScript struct {
|
||||||
|
*plugin.Plugin
|
||||||
|
|
||||||
|
doOnRequest bool
|
||||||
|
doOnResponse bool
|
||||||
|
doOnCommand bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadDnsProxyScript(path string, sess *session.Session) (err error, s *DnsProxyScript) {
|
||||||
|
log.Debug("loading proxy script %s ...", path)
|
||||||
|
|
||||||
|
plug, err := plugin.Load(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// define session pointer
|
||||||
|
if err = plug.Set("env", sess.Env.Data); err != nil {
|
||||||
|
log.Error("Error while defining environment: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// define addSessionEvent function
|
||||||
|
err = plug.Set("addSessionEvent", func(call otto.FunctionCall) otto.Value {
|
||||||
|
if len(call.ArgumentList) < 2 {
|
||||||
|
log.Error("Failed to execute 'addSessionEvent' in DNS proxy: 2 arguments required, but only %d given.", len(call.ArgumentList))
|
||||||
|
return otto.FalseValue()
|
||||||
|
}
|
||||||
|
ottoTag := call.Argument(0)
|
||||||
|
if !ottoTag.IsString() {
|
||||||
|
log.Error("Failed to execute 'addSessionEvent' in DNS proxy: first argument must be a string.")
|
||||||
|
return otto.FalseValue()
|
||||||
|
}
|
||||||
|
tag := strings.TrimSpace(ottoTag.String())
|
||||||
|
if tag == "" {
|
||||||
|
log.Error("Failed to execute 'addSessionEvent' in DNS proxy: tag cannot be empty.")
|
||||||
|
return otto.FalseValue()
|
||||||
|
}
|
||||||
|
data := call.Argument(1)
|
||||||
|
sess.Events.Add(tag, data)
|
||||||
|
return otto.TrueValue()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error while defining addSessionEvent function: %+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// run onLoad if defined
|
||||||
|
if plug.HasFunc("onLoad") {
|
||||||
|
if _, err = plug.Call("onLoad"); err != nil {
|
||||||
|
log.Error("Error while executing onLoad callback: %s", "\nTraceback:\n "+err.(*otto.Error).String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s = &DnsProxyScript{
|
||||||
|
Plugin: plug,
|
||||||
|
doOnRequest: plug.HasFunc("onRequest"),
|
||||||
|
doOnResponse: plug.HasFunc("onResponse"),
|
||||||
|
doOnCommand: plug.HasFunc("onCommand"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DnsProxyScript) OnRequest(req *dns.Msg, clientIP string) (jsreq, jsres *JSQuery) {
|
||||||
|
if s.doOnRequest {
|
||||||
|
jsreq := NewJSQuery(req, clientIP)
|
||||||
|
jsres := NewJSQuery(req, clientIP)
|
||||||
|
|
||||||
|
if _, err := s.Call("onRequest", jsreq, jsres); err != nil {
|
||||||
|
log.Error("%s", err)
|
||||||
|
return nil, nil
|
||||||
|
} else if jsreq.WasModified() {
|
||||||
|
jsreq.UpdateHash()
|
||||||
|
return jsreq, nil
|
||||||
|
} else if jsres.WasModified() {
|
||||||
|
jsres.UpdateHash()
|
||||||
|
return nil, jsres
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DnsProxyScript) OnResponse(req, res *dns.Msg, clientIP string) (jsreq, jsres *JSQuery) {
|
||||||
|
if s.doOnResponse {
|
||||||
|
jsreq := NewJSQuery(req, clientIP)
|
||||||
|
jsres := NewJSQuery(res, clientIP)
|
||||||
|
|
||||||
|
if _, err := s.Call("onResponse", jsreq, jsres); err != nil {
|
||||||
|
log.Error("%s", err)
|
||||||
|
return nil, nil
|
||||||
|
} else if jsres.WasModified() {
|
||||||
|
jsres.UpdateHash()
|
||||||
|
return nil, jsres
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DnsProxyScript) OnCommand(cmd string) bool {
|
||||||
|
if s.doOnCommand {
|
||||||
|
if ret, err := s.Call("onCommand", cmd); err != nil {
|
||||||
|
log.Error("Error while executing onCommand callback: %+v", err)
|
||||||
|
return false
|
||||||
|
} else if v, ok := ret.(bool); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/bettercap/bettercap/v2/modules/can"
|
"github.com/bettercap/bettercap/v2/modules/can"
|
||||||
"github.com/bettercap/bettercap/v2/modules/caplets"
|
"github.com/bettercap/bettercap/v2/modules/caplets"
|
||||||
"github.com/bettercap/bettercap/v2/modules/dhcp6_spoof"
|
"github.com/bettercap/bettercap/v2/modules/dhcp6_spoof"
|
||||||
|
"github.com/bettercap/bettercap/v2/modules/dns_proxy"
|
||||||
"github.com/bettercap/bettercap/v2/modules/dns_spoof"
|
"github.com/bettercap/bettercap/v2/modules/dns_spoof"
|
||||||
"github.com/bettercap/bettercap/v2/modules/events_stream"
|
"github.com/bettercap/bettercap/v2/modules/events_stream"
|
||||||
"github.com/bettercap/bettercap/v2/modules/gps"
|
"github.com/bettercap/bettercap/v2/modules/gps"
|
||||||
|
@ -45,6 +46,7 @@ func LoadModules(sess *session.Session) {
|
||||||
sess.Register(can.NewCanModule(sess))
|
sess.Register(can.NewCanModule(sess))
|
||||||
sess.Register(dhcp6_spoof.NewDHCP6Spoofer(sess))
|
sess.Register(dhcp6_spoof.NewDHCP6Spoofer(sess))
|
||||||
sess.Register(net_recon.NewDiscovery(sess))
|
sess.Register(net_recon.NewDiscovery(sess))
|
||||||
|
sess.Register(dns_proxy.NewDnsProxy(sess))
|
||||||
sess.Register(dns_spoof.NewDNSSpoofer(sess))
|
sess.Register(dns_spoof.NewDNSSpoofer(sess))
|
||||||
sess.Register(events_stream.NewEventsStream(sess))
|
sess.Register(events_stream.NewEventsStream(sess))
|
||||||
sess.Register(gps.NewGPS(sess))
|
sess.Register(gps.NewGPS(sess))
|
||||||
|
|
|
@ -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