1
0
mirror of https://github.com/bettercap/bettercap.git synced 2025-01-06 03:00:15 -08:00

Add TLS support for DNS proxy, implement backwards compatible DNS record conversion.

This commit is contained in:
buffermet 2024-10-09 13:47:21 +02:00
parent a49d561dce
commit 43f1013f0d
8 changed files with 1603 additions and 145 deletions

View File

@ -2,6 +2,10 @@ 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 {
@ -22,6 +26,10 @@ func (mod *DnsProxy) Configure() error {
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())
@ -29,21 +37,56 @@ func (mod *DnsProxy) Configure() error {
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, netProtocol = mod.StringParam("dns.proxy.networkprotocol"); 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
}
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 {
@ -69,24 +112,42 @@ func NewDnsProxy(s *session.Session) *DnsProxy {
session.IPv4Validator,
"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",
"1.1.1.1",
session.IPv4Validator,
"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",
"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",
"",
"",

View File

@ -2,7 +2,10 @@ package dns_proxy
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"strings"
"time"
@ -20,13 +23,17 @@ const (
)
type DNSProxy struct {
Address string
Name string
Address string
Server *dns.Server
Redirection *firewall.Redirection
Nameserver string
NetProtocol string
Redirection *firewall.Redirection
Script *DnsProxyScript
Server *dns.Server
CertFile string
KeyFile string
Blacklist []string
Whitelist []string
Sess *session.Session
doRedirect bool
@ -34,11 +41,13 @@ type DNSProxy struct {
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
p.Address = address
p.doRedirect = doRedirect
p.CertFile = certFile
p.KeyFile = keyFile
if scriptPath != "" {
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)
if res == nil {
p.Error("response filter returned 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)
@ -98,14 +108,35 @@ func (p *DNSProxy) Configure(address string, dnsPort int, doRedirect bool, names
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(),
netProtocol,
redirectProtocol,
dnsPort,
p.Address,
proxyPort)

View File

@ -6,43 +6,60 @@ import (
"github.com/miekg/dns"
)
func shortenResourceRecords(records []string) []string {
shorterRecords := []string{}
for _, record := range records {
shorterRecord := strings.ReplaceAll(record, "\t", " ")
shorterRecords = append(shorterRecords, shorterRecord)
func questionsToStrings(qs []dns.Question) []string {
questions := []string{}
for _, q := range qs {
questions = append(questions, tabsToSpaces(q.String()))
}
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 {
Client string
Questions []string
}{
j.Client["IP"],
shortenResourceRecords(j.Questions),
clientIP,
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 {
client string
Extras []string
Answers []string
Extras []string
Nameservers []string
Questions []string
}{
j.Client["IP"],
shortenResourceRecords(j.Extras),
shortenResourceRecords(j.Answers),
shortenResourceRecords(j.Nameservers),
shortenResourceRecords(j.Questions),
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) {
p.Debug("< %s %s", clientIP, query.Question)
p.Debug("< %s %s",
clientIP,
strings.Join(questionsToStrings(query.Question), ","))
// do we have a proxy script?
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)
if jsreq != nil {
// the request has been changed by the script
p.logRequestAction(jsreq)
return jsreq.ToQuery(), nil
req := jsreq.ToQuery()
p.logRequestAction(req, clientIP)
return req, nil
} else if jsres != nil {
// a fake response has been returned by the script
p.logResponseAction(jsres)
return query, jsres.ToQuery()
res := jsres.ToQuery()
p.logResponseAction(res, clientIP)
return query, res
}
return query, nil
@ -70,15 +89,21 @@ func (p *DNSProxy) onResponseFilter(req, res *dns.Msg, clientIP string) *dns.Msg
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?
if p.Script != nil {
_, jsres := p.Script.OnResponse(req, res, clientIP)
if jsres != nil {
// the response has been changed by the script
p.logResponseAction(jsres)
return jsres.ToQuery()
res := jsres.ToQuery()
p.logResponseAction(res, clientIP)
return res
}
}

View File

@ -1,9 +1,8 @@
package dns_proxy
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/bettercap/bettercap/v2/log"
"github.com/bettercap/bettercap/v2/session"
@ -11,17 +10,14 @@ import (
"github.com/miekg/dns"
)
var whiteSpaceRegexp = regexp.MustCompile(`\s+`)
var stripWhiteSpaceRegexp = regexp.MustCompile(`^\s*(.*?)\s*$`)
type JSQuery struct {
Answers []string
Answers []map[string]interface{}
Client map[string]string
Compress bool `json:"-"`
Extras []string
Header *JSQueryHeader
Nameservers []string
Questions []string
Compress bool
Extras []map[string]interface{}
Header JSQueryHeader
Nameservers []map[string]interface{}
Questions []map[string]interface{}
refHash string
}
@ -41,6 +37,11 @@ type JSQueryHeader struct {
}
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,
@ -53,50 +54,58 @@ func (j *JSQuery) NewHash() string {
j.Header.Response,
j.Header.Truncated,
j.Header.Zero)
hash := fmt.Sprintf("%s.%s.%t.%s.%s.%s.%s",
strings.Join(j.Answers, ""),
answers,
j.Client["IP"],
j.Compress,
strings.Join(j.Extras, ""),
extras,
headerHash,
strings.Join(j.Nameservers, ""),
strings.Join(j.Questions, ""))
nameservers,
questions)
return hash
}
func NewJSQuery(query *dns.Msg, clientIP string) *JSQuery {
answers := []string{}
extras := []string{}
nameservers := []string{}
questions := []string{}
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))
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,
for i, rr := range query.Answer {
jsRecord, err := NewJSResourceRecord(rr)
if err != nil {
log.Error(err.Error())
continue
}
answers[i] = jsRecord
}
for _, rr := range query.Answer {
answers = append(answers, rr.String())
for i, rr := range query.Extra {
jsRecord, err := NewJSResourceRecord(rr)
if err != nil {
log.Error(err.Error())
continue
}
extras[i] = jsRecord
}
for _, rr := range query.Extra {
extras = append(extras, rr.String())
for i, rr := range query.Ns {
jsRecord, err := NewJSResourceRecord(rr)
if err != nil {
log.Error(err.Error())
continue
}
nameservers[i] = jsRecord
}
for _, rr := range query.Ns {
nameservers = append(nameservers, rr.String())
}
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))
for i, question := range query.Question {
questions[i] = map[string]interface{}{
"Name": question.Name,
"Qtype": question.Qtype,
"Qclass": question.Qclass,
}
}
clientMAC := ""
@ -108,11 +117,23 @@ func NewJSQuery(query *dns.Msg, clientIP string) *JSQuery {
client := map[string]string{"IP": clientIP, "MAC": clientMAC, "Alias": clientAlias}
jsquery := &JSQuery{
Answers: answers,
Client: client,
Compress: query.Compress,
Extras: extras,
Header: header,
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,
}
@ -121,80 +142,43 @@ func NewJSQuery(query *dns.Msg, clientIP string) *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 {
var answers []dns.RR
var extras []dns.RR
var nameservers []dns.RR
var questions []dns.Question
for _, s := range j.Answers {
rr, err := dns.NewRR(s)
for _, jsRR := range j.Answers {
rr, err := ToRR(jsRR)
if err != nil {
log.Error("error parsing DNS answer resource record: %s", err.Error())
return nil
} else {
answers = append(answers, rr)
log.Error(err.Error())
continue
}
answers = append(answers, rr)
}
for _, s := range j.Extras {
rr, err := dns.NewRR(s)
for _, jsRR := range j.Extras {
rr, err := ToRR(jsRR)
if err != nil {
log.Error("error parsing DNS extra resource record: %s", err.Error())
return nil
} else {
extras = append(extras, rr)
log.Error(err.Error())
continue
}
extras = append(extras, rr)
}
for _, s := range j.Nameservers {
rr, err := dns.NewRR(s)
for _, jsRR := range j.Nameservers {
rr, err := ToRR(jsRR)
if err != nil {
log.Error("error parsing DNS nameserver resource record: %s", err.Error())
return nil
} else {
nameservers = append(nameservers, rr)
log.Error(err.Error())
continue
}
nameservers = append(nameservers, rr)
}
for _, s := range j.Questions {
qStripped := stripWhiteSpaceRegexp.FindStringSubmatch(s)
qParts := whiteSpaceRegexp.Split(qStripped[1], -1)
if len(qParts) != 3 {
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})
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{

File diff suppressed because it is too large Load Diff

View 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
}

View 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
}

View File

@ -40,6 +40,14 @@ var (
OrganizationalUnit: "https://certs.godaddy.com/repository/",
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) {