mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 10:06:24 +05:00
755 lines
20 KiB
Go
755 lines
20 KiB
Go
/*
|
|
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
|
|
Copyright (C) ITsysCOM GmbH
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
package agents
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/cgrates/cgrates/config"
|
|
"github.com/cgrates/cgrates/utils"
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
func newDnsReply(req *dns.Msg) (rply *dns.Msg) {
|
|
rply = new(dns.Msg)
|
|
rply.SetReply(req)
|
|
if len(req.Question) > 0 {
|
|
rply.Question = make([]dns.Question, len(req.Question))
|
|
copy(rply.Question, req.Question)
|
|
}
|
|
if opts := rply.IsEdns0(); opts != nil {
|
|
rply.SetEdns0(4096, false).IsEdns0().Option = opts.Option
|
|
}
|
|
return
|
|
}
|
|
|
|
// dnsWriteErr writes the error with code back to the client
|
|
func dnsWriteMsg(w dns.ResponseWriter, msg *dns.Msg) (err error) {
|
|
if err = w.WriteMsg(msg); err != nil {
|
|
utils.Logger.Warning(
|
|
fmt.Sprintf("<%s> error: <%s> when writing on connection",
|
|
utils.DNSAgent, err.Error()))
|
|
}
|
|
return
|
|
}
|
|
|
|
func newDnsDP(req *dns.Msg) utils.DataProvider {
|
|
return &dnsDP{
|
|
req: config.NewObjectDP(req),
|
|
opts: config.NewObjectDP(req.IsEdns0()),
|
|
}
|
|
}
|
|
|
|
type dnsDP struct {
|
|
req utils.DataProvider
|
|
opts utils.DataProvider
|
|
}
|
|
|
|
func (dp dnsDP) String() string { return dp.req.String() }
|
|
func (dp dnsDP) FieldAsInterface(fldPath []string) (o any, e error) {
|
|
if len(fldPath) != 0 && strings.HasPrefix(fldPath[0], utils.DNSOption) {
|
|
return dp.opts.FieldAsInterface(fldPath)
|
|
}
|
|
return dp.req.FieldAsInterface(fldPath)
|
|
}
|
|
func (dp dnsDP) FieldAsString(fldPath []string) (string, error) {
|
|
valIface, err := dp.FieldAsInterface(fldPath)
|
|
if err != nil {
|
|
return utils.EmptyString, err
|
|
}
|
|
return utils.IfaceAsString(valIface), nil
|
|
}
|
|
|
|
func updateDNSMsgFromNM(msg *dns.Msg, nm *utils.OrderedNavigableMap, qType uint16, qName string) (err error) {
|
|
msgFields := make(utils.StringSet) // work around to NMap issue
|
|
for el := nm.GetFirstElement(); el != nil; el = el.Next() {
|
|
path := el.Value
|
|
itm, _ := nm.Field(path)
|
|
switch path[0] { // go for each posible field
|
|
case utils.DNSId:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.Id = uint16(vItm)
|
|
case utils.DNSResponse:
|
|
var vItm bool
|
|
if vItm, err = utils.IfaceAsBool(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.Response = vItm
|
|
case utils.DNSOpcode:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.Opcode = int(vItm)
|
|
case utils.DNSAuthoritative:
|
|
var vItm bool
|
|
if vItm, err = utils.IfaceAsBool(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.Authoritative = vItm
|
|
case utils.DNSTruncated:
|
|
var vItm bool
|
|
if vItm, err = utils.IfaceAsBool(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.Truncated = vItm
|
|
case utils.DNSRecursionDesired:
|
|
var vItm bool
|
|
if vItm, err = utils.IfaceAsBool(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.RecursionDesired = vItm
|
|
case utils.DNSRecursionAvailable:
|
|
var vItm bool
|
|
if vItm, err = utils.IfaceAsBool(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.RecursionAvailable = vItm
|
|
case utils.DNSZero:
|
|
var vItm bool
|
|
if vItm, err = utils.IfaceAsBool(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.Zero = vItm
|
|
case utils.DNSAuthenticatedData:
|
|
var vItm bool
|
|
if vItm, err = utils.IfaceAsBool(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.AuthenticatedData = vItm
|
|
case utils.DNSCheckingDisabled:
|
|
var vItm bool
|
|
if vItm, err = utils.IfaceAsBool(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.CheckingDisabled = vItm
|
|
case utils.DNSRcode:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(itm.Data); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[0], err.Error())
|
|
}
|
|
msg.Rcode = int(vItm)
|
|
case utils.DNSQuestion:
|
|
if msg.Question, err = updateDnsQuestions(msg.Question, path[1:len(path)-1], itm.Data, itm.NewBranch); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[:len(path)-1], err.Error())
|
|
}
|
|
case utils.DNSAnswer:
|
|
newBranch := itm.NewBranch ||
|
|
len(msg.Answer) == 0 ||
|
|
msgFields.Has(path[0])
|
|
if newBranch { // force append if the same path was already used
|
|
msgFields = make(utils.StringSet) // reset the fields inside since we have a new message
|
|
msgFields.Add(strings.Join(path, ".")) // detect new branch
|
|
}
|
|
if msg.Answer, err = updateDnsAnswer(msg.Answer, qType, qName, path[1:len(path)-1], itm.Data, newBranch); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[:len(path)-1], err.Error())
|
|
}
|
|
case utils.DNSNs: //ToDO
|
|
case utils.DNSExtra: //ToDO
|
|
case utils.DNSOption:
|
|
opts := msg.IsEdns0()
|
|
if opts == nil {
|
|
opts = msg.SetEdns0(4096, false).IsEdns0()
|
|
}
|
|
if opts.Option, err = updateDnsOption(opts.Option, path[1:len(path)-1], itm.Data, itm.NewBranch); err != nil {
|
|
return fmt.Errorf("item: <%s>, err: %s", path[:len(path)-1], err.Error())
|
|
}
|
|
default:
|
|
}
|
|
|
|
}
|
|
return
|
|
}
|
|
|
|
// updateDnsQuestion
|
|
func updateDnsQuestions(q []dns.Question, path []string, value any, newBranch bool) (_ []dns.Question, err error) {
|
|
var idx int
|
|
var field string
|
|
switch len(path) {
|
|
case 1: // only the field so update the last one
|
|
if newBranch || len(q) == 0 {
|
|
q = append(q, dns.Question{})
|
|
}
|
|
idx = len(q) - 1
|
|
field = path[0]
|
|
case 2: // the index is specified
|
|
if idx, err = strconv.Atoi(path[0]); err != nil {
|
|
return
|
|
}
|
|
if lq := len(q); idx > lq {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
} else if lq == idx {
|
|
q = append(q, dns.Question{})
|
|
}
|
|
field = path[1]
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
switch field {
|
|
case utils.DNSName:
|
|
q[idx].Name = utils.IfaceAsString(value)
|
|
case utils.DNSQtype:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
q[idx].Qtype = uint16(vItm)
|
|
case utils.DNSQclass:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
q[idx].Qclass = uint16(vItm)
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
|
|
return q, nil
|
|
}
|
|
|
|
func updateDnsOption(q []dns.EDNS0, path []string, value any, newBranch bool) (_ []dns.EDNS0, err error) {
|
|
var idx int
|
|
var field string
|
|
switch len(path) {
|
|
case 1: // only the field so update the last one
|
|
field = path[0]
|
|
if newBranch ||
|
|
len(q) == 0 {
|
|
var o dns.EDNS0
|
|
if o, err = createDnsOption(field, value); err != nil {
|
|
return
|
|
}
|
|
return append(q, o), nil
|
|
}
|
|
idx = len(q) - 1
|
|
case 2: // the index is specified
|
|
field = path[1]
|
|
if idx, err = strconv.Atoi(path[0]); err != nil {
|
|
return
|
|
}
|
|
if lq := len(q); idx > lq {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
} else if lq == idx {
|
|
var o dns.EDNS0
|
|
if o, err = createDnsOption(field, value); err != nil {
|
|
return
|
|
}
|
|
return append(q, o), nil
|
|
}
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
switch v := q[idx].(type) {
|
|
case *dns.EDNS0_NSID:
|
|
if field != utils.DNSNsid {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
v.Nsid = utils.IfaceAsString(value)
|
|
case *dns.EDNS0_SUBNET:
|
|
switch field {
|
|
case utils.DNSFamily:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Family = uint16(vItm)
|
|
case utils.DNSSourceNetmask:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.SourceNetmask = uint8(vItm)
|
|
case utils.DNSSourceScope:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.SourceScope = uint8(vItm)
|
|
case utils.Address:
|
|
v.Address = net.ParseIP(utils.IfaceAsString(value))
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
case *dns.EDNS0_COOKIE:
|
|
if field != utils.DNSCookie {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
v.Cookie = utils.IfaceAsString(value)
|
|
case *dns.EDNS0_UL:
|
|
switch field {
|
|
case utils.DNSLease:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Lease = uint32(vItm)
|
|
case utils.DNSKeyLease:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.KeyLease = uint32(vItm)
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
case *dns.EDNS0_LLQ:
|
|
switch field {
|
|
case utils.VersionName:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Version = uint16(vItm)
|
|
case utils.DNSOpcode:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Opcode = uint16(vItm)
|
|
case utils.Error:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Error = uint16(vItm)
|
|
case utils.DNSId:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Id = uint64(vItm)
|
|
case utils.DNSLeaseLife:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.LeaseLife = uint32(vItm)
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
case *dns.EDNS0_DAU:
|
|
if field != utils.DNSDAU {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
v.AlgCode = []uint8(utils.IfaceAsString(value))
|
|
case *dns.EDNS0_DHU:
|
|
if field != utils.DNSDHU {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
v.AlgCode = []uint8(utils.IfaceAsString(value))
|
|
case *dns.EDNS0_N3U:
|
|
if field != utils.DNSN3U {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
v.AlgCode = []uint8(utils.IfaceAsString(value))
|
|
case *dns.EDNS0_EXPIRE:
|
|
if field != utils.DNSExpire {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Expire = uint32(vItm)
|
|
case *dns.EDNS0_TCP_KEEPALIVE:
|
|
switch field {
|
|
case utils.Length: //
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Length = uint16(vItm)
|
|
case utils.DNSTimeout: //
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Timeout = uint16(vItm)
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
case *dns.EDNS0_PADDING:
|
|
if field != utils.DNSPadding {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
v.Padding = []byte(utils.IfaceAsString(value))
|
|
case *dns.EDNS0_EDE:
|
|
switch field {
|
|
case utils.DNSInfoCode:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.InfoCode = uint16(vItm)
|
|
case utils.DNSExtraText:
|
|
v.ExtraText = utils.IfaceAsString(value)
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
case *dns.EDNS0_ESU:
|
|
if field != utils.DNSUri {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
v.Uri = utils.IfaceAsString(value)
|
|
case *dns.EDNS0_LOCAL: // if already there you can change data
|
|
if field != utils.DNSData {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
v.Data = []byte(utils.IfaceAsString(value))
|
|
case nil:
|
|
err = fmt.Errorf("unsupported dns option type <%T>", v)
|
|
default:
|
|
err = fmt.Errorf("unsupported dns option type <%T>", v)
|
|
}
|
|
return q, err
|
|
}
|
|
|
|
func createDnsOption(field string, value any) (o dns.EDNS0, err error) {
|
|
switch field {
|
|
case utils.DNSNsid: // EDNS0_NSID
|
|
o = &dns.EDNS0_NSID{Nsid: utils.IfaceAsString(value)}
|
|
case utils.DNSFamily: // EDNS0_SUBNET
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_SUBNET{Family: uint16(vItm)}
|
|
case utils.DNSSourceNetmask: // EDNS0_SUBNET
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_SUBNET{SourceNetmask: uint8(vItm)}
|
|
case utils.DNSSourceScope: // EDNS0_SUBNET
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_SUBNET{SourceScope: uint8(vItm)}
|
|
case utils.Address: // EDNS0_SUBNET
|
|
o = &dns.EDNS0_SUBNET{Address: net.ParseIP(utils.IfaceAsString(value))}
|
|
case utils.DNSCookie: // EDNS0_COOKIE
|
|
o = &dns.EDNS0_COOKIE{Cookie: utils.IfaceAsString(value)}
|
|
case utils.DNSLease: // EDNS0_UL
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_UL{Lease: uint32(vItm)}
|
|
case utils.DNSKeyLease: // EDNS0_UL
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_UL{KeyLease: uint32(vItm)}
|
|
case utils.VersionName: // EDNS0_LLQ
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_LLQ{Version: uint16(vItm)}
|
|
case utils.DNSOpcode: // EDNS0_LLQ
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_LLQ{Opcode: uint16(vItm)}
|
|
case utils.Error: // EDNS0_LLQ
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_LLQ{Error: uint16(vItm)}
|
|
case utils.DNSId: // EDNS0_LLQ
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_LLQ{Id: uint64(vItm)}
|
|
case utils.DNSLeaseLife: // EDNS0_LLQ
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_LLQ{LeaseLife: uint32(vItm)}
|
|
case utils.DNSDAU: // EDNS0_DAU
|
|
o = &dns.EDNS0_DAU{AlgCode: []uint8(utils.IfaceAsString(value))}
|
|
case utils.DNSDHU: // EDNS0_DHU
|
|
o = &dns.EDNS0_DHU{AlgCode: []uint8(utils.IfaceAsString(value))}
|
|
case utils.DNSN3U: // EDNS0_N3U
|
|
o = &dns.EDNS0_N3U{AlgCode: []uint8(utils.IfaceAsString(value))}
|
|
case utils.DNSExpire: // EDNS0_EXPIRE
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_EXPIRE{Expire: uint32(vItm)}
|
|
case utils.Length: // EDNS0_TCP_KEEPALIVE
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_TCP_KEEPALIVE{Length: uint16(vItm)}
|
|
case utils.DNSTimeout: // EDNS0_TCP_KEEPALIVE
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_TCP_KEEPALIVE{Timeout: uint16(vItm)}
|
|
case utils.DNSPadding: // EDNS0_PADDING
|
|
o = &dns.EDNS0_PADDING{Padding: []byte(utils.IfaceAsString(value))}
|
|
case utils.DNSInfoCode: // EDNS0_EDE
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
o = &dns.EDNS0_EDE{InfoCode: uint16(vItm)}
|
|
case utils.DNSExtraText: // EDNS0_EDE
|
|
o = &dns.EDNS0_EDE{ExtraText: utils.IfaceAsString(value)}
|
|
case utils.DNSUri: // EDNS0_ESU
|
|
o = &dns.EDNS0_ESU{Uri: utils.IfaceAsString(value)}
|
|
default:
|
|
err = fmt.Errorf("can not create option from field <%q>", field)
|
|
}
|
|
return
|
|
}
|
|
|
|
func updateDnsAnswer(q []dns.RR, qType uint16, qName string, path []string, value any, newBranch bool) (_ []dns.RR, err error) {
|
|
var idx int
|
|
if lPath := len(path); lPath == 0 {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
} else {
|
|
var hasIdx bool
|
|
if idx, err = strconv.Atoi(path[0]); err == nil {
|
|
hasIdx = true
|
|
}
|
|
err = nil
|
|
if !hasIdx || lPath == 1 { // only the field so update the last one
|
|
if newBranch || len(q) == 0 {
|
|
var a dns.RR
|
|
if a, err = newDNSAnswer(qType, qName); err != nil {
|
|
return
|
|
}
|
|
q = append(q, a)
|
|
}
|
|
idx = len(q) - 1
|
|
} else { // the index is specified
|
|
if lq := len(q); idx > lq {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
} else if lq == idx {
|
|
var a dns.RR
|
|
if a, err = newDNSAnswer(qType, qName); err != nil {
|
|
return
|
|
}
|
|
q = append(q, a)
|
|
}
|
|
path = path[1:]
|
|
}
|
|
}
|
|
|
|
switch v := q[idx].(type) {
|
|
case *dns.NAPTR:
|
|
err = updateDnsNAPTRAnswer(v, path, value)
|
|
case *dns.SRV:
|
|
err = updateDnsSRVAnswer(v, path, value)
|
|
case *dns.A:
|
|
if len(path) < 1 ||
|
|
(path[0] != utils.DNSHdr && len(path) != 1) ||
|
|
(path[0] == utils.DNSHdr && len(path) != 2) {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
switch path[0] {
|
|
case utils.DNSHdr:
|
|
err = updateDnsRRHeader(&v.Hdr, path[1:], value)
|
|
case utils.DNSA:
|
|
v.A = net.ParseIP(utils.IfaceAsString(value))
|
|
if v.A == nil {
|
|
err = fmt.Errorf("invalid IP address <%v>",
|
|
utils.IfaceAsString(value))
|
|
return
|
|
}
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
}
|
|
case nil:
|
|
err = fmt.Errorf("unsupported dns option type <%T>", v)
|
|
default:
|
|
err = fmt.Errorf("unsupported dns option type <%T>", v)
|
|
}
|
|
|
|
return q, err
|
|
}
|
|
|
|
// appendDNSAnswer will append the right answer payload to the message
|
|
func newDNSAnswer(qType uint16, qName string) (a dns.RR, err error) {
|
|
hdr := dns.RR_Header{
|
|
Name: qName,
|
|
Rrtype: qType,
|
|
Class: dns.ClassINET,
|
|
Ttl: 60,
|
|
}
|
|
switch qType {
|
|
case dns.TypeA:
|
|
a = &dns.A{Hdr: hdr}
|
|
case dns.TypeNAPTR:
|
|
a = &dns.NAPTR{Hdr: hdr}
|
|
case dns.TypeSRV:
|
|
a = &dns.SRV{Hdr: hdr}
|
|
default:
|
|
err = fmt.Errorf("unsupported DNS type: <%v>", dns.TypeToString[qType])
|
|
}
|
|
return
|
|
}
|
|
|
|
func updateDnsSRVAnswer(v *dns.SRV, path []string, value any) (err error) {
|
|
if len(path) < 1 ||
|
|
(path[0] != utils.DNSHdr && len(path) != 1) ||
|
|
(path[0] == utils.DNSHdr && len(path) != 2) {
|
|
err = utils.ErrWrongPath
|
|
return
|
|
}
|
|
switch path[0] {
|
|
case utils.DNSHdr:
|
|
err = updateDnsRRHeader(&v.Hdr, path[1:], value)
|
|
case utils.DNSPriority:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Priority = uint16(vItm)
|
|
case utils.Weight:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Weight = uint16(vItm)
|
|
case utils.DNSPort:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Port = uint16(vItm)
|
|
case utils.DNSTarget:
|
|
v.Target = utils.IfaceAsString(value)
|
|
default:
|
|
err = utils.ErrWrongPath
|
|
}
|
|
return
|
|
}
|
|
|
|
func updateDnsNAPTRAnswer(v *dns.NAPTR, path []string, value any) (err error) {
|
|
if len(path) < 1 ||
|
|
(path[0] != utils.DNSHdr && len(path) != 1) ||
|
|
(path[0] == utils.DNSHdr && len(path) != 2) {
|
|
return utils.ErrWrongPath
|
|
}
|
|
switch path[0] {
|
|
case utils.DNSHdr:
|
|
return updateDnsRRHeader(&v.Hdr, path[1:], value)
|
|
case utils.Order:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Order = uint16(vItm)
|
|
case utils.Preference:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Preference = uint16(vItm)
|
|
case utils.Flags:
|
|
v.Flags = utils.IfaceAsString(value)
|
|
case utils.Service:
|
|
v.Service = utils.IfaceAsString(value)
|
|
case utils.Regexp:
|
|
v.Regexp = utils.IfaceAsString(value)
|
|
case utils.Replacement:
|
|
v.Replacement = utils.IfaceAsString(value)
|
|
default:
|
|
return utils.ErrWrongPath
|
|
}
|
|
return
|
|
}
|
|
|
|
func updateDnsRRHeader(v *dns.RR_Header, path []string, value any) (err error) {
|
|
if len(path) != 1 {
|
|
return utils.ErrWrongPath
|
|
}
|
|
switch path[0] {
|
|
case utils.DNSName:
|
|
v.Name = utils.IfaceAsString(value)
|
|
case utils.DNSRrtype:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Rrtype = uint16(vItm)
|
|
case utils.DNSClass:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Class = uint16(vItm)
|
|
case utils.DNSTtl:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Ttl = uint32(vItm)
|
|
case utils.DNSRdlength:
|
|
var vItm int64
|
|
if vItm, err = utils.IfaceAsTInt64(value); err != nil {
|
|
return
|
|
}
|
|
v.Rdlength = uint16(vItm)
|
|
default:
|
|
return utils.ErrWrongPath
|
|
}
|
|
return
|
|
}
|