DNSAgent - basic ENUM implementation working with dryRun

This commit is contained in:
DanB
2019-04-26 15:59:02 +02:00
parent da8d971f52
commit f6a9ec0ab6
6 changed files with 128 additions and 68 deletions

View File

@@ -72,28 +72,6 @@ func (da *DNSAgent) ListenAndServe() error {
// handleMessage is the entry point of all DNS requests
// requests are reaching here asynchronously
func (da *DNSAgent) handleMessage(w dns.ResponseWriter, req *dns.Msg) {
fmt.Printf("got message: %+v\n", req)
/*rply := new(dns.Msg)
rply.SetReply(req)
switch req.Question[0].Qtype {
case dns.TypeA:
rply.Authoritative = true
if req.Question[0].Name == "cgrates.org." {
rply.Answer = append(rply.Answer,
&dns.A{
Hdr: dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 60},
A: net.ParseIP("195.201.167.179")},
)
}
}
rply.Rcode = dns.RcodeServerFailure
w.WriteMsg(rply)
*/
dnsDP := newDNSDataProvider(req, w)
reqVars := make(map[string]interface{})
reqVars[QueryType] = dns.TypeToString[req.Question[0].Qtype]
@@ -112,10 +90,8 @@ func (da *DNSAgent) handleMessage(w dns.ResponseWriter, req *dns.Msg) {
return
}
reqVars[E164Address] = e164
utils.Logger.Info(fmt.Sprintf("reqVars: %+v", reqVars))
}
rplyNM := config.NewNavigableMap(nil) // share it among different processors
var processed bool
var err error
for _, reqProcessor := range da.cgrCfg.DNSAgentCfg().RequestProcessors {
@@ -152,6 +128,20 @@ func (da *DNSAgent) handleMessage(w dns.ResponseWriter, req *dns.Msg) {
dnsWriteMsg(w, rply)
return
}
if err = updateDNSMsgFromNM(rply, rplyNM); err != nil {
utils.Logger.Warning(
fmt.Sprintf("<%s> error: %s updating answer: %s from NM %s",
utils.DNSAgent, err.Error(), utils.ToJSON(rply), utils.ToJSON(rplyNM)))
rply.Rcode = dns.RcodeServerFailure
dnsWriteMsg(w, rply)
}
if err = dnsWriteMsg(w, rply); err != nil { // failed sending, most probably content issue
rply = new(dns.Msg)
rply.SetReply(req)
rply.Rcode = dns.RcodeServerFailure
dnsWriteMsg(w, rply)
}
return
}
func (da *DNSAgent) processRequest(reqProcessor *config.RequestProcessor,

View File

@@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package agents
import (
"fmt"
"net/rpc"
"net/rpc/jsonrpc"
"path"
@@ -43,7 +42,7 @@ var (
var sTestsDNS = []func(t *testing.T){
testDNSitResetDB,
testDNSitStartEngine,
//testDNSitStartEngine,
testDNSitApierRpcConn,
testDNSitTPFromFolder,
testDNSitClntConn,
@@ -94,26 +93,13 @@ func testDNSitApierRpcConn(t *testing.T) {
// Load the tariff plan, creating accounts and their balances
func testDNSitTPFromFolder(t *testing.T) {
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "oldtutorial")}
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
var loadInst utils.LoadInstance
if err := dnsRPC.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil {
if err := dnsRPC.Call(utils.ApierV2LoadTariffPlanFromFolder,
attrs, &loadInst); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
//add a default charger
chargerProfile := &engine.ChargerProfile{
Tenant: "cgrates.org",
ID: "Default",
RunID: utils.MetaDefault,
AttributeIDs: []string{"*none"},
Weight: 20,
}
var result string
if err := dnsRPC.Call("ApierV1.SetChargerProfile", chargerProfile, &result); err != nil {
t.Error(err)
} else if result != utils.OK {
t.Error("Unexpected reply returned", result)
}
}
// Connect DNS client to server
@@ -125,7 +111,6 @@ func testDNSitClntConn(t *testing.T) {
} else if dnsClnt == nil {
t.Fatalf("conn is nil")
}
fmt.Printf("done connecting to %s", dnsCfg.DNSAgentCfg().Listen)
}
func testDNSitClntNAPTRDryRun(t *testing.T) {
@@ -140,6 +125,25 @@ func testDNSitClntNAPTRDryRun(t *testing.T) {
if rply.Rcode != dns.RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", rply)
}
answr := rply.Answer[0].(*dns.NAPTR)
if answr.Order != 100 {
t.Errorf("received: <%q>", answr.Order)
}
if answr.Preference != 10 {
t.Errorf("received: <%q>", answr.Preference)
}
if answr.Flags != "U" {
t.Errorf("received: <%q>", answr.Flags)
}
if answr.Service != "E2U+SIP" {
t.Errorf("received: <%q>", answr.Service)
}
if answr.Regexp != "^.*$" {
t.Errorf("received: <%q>", answr.Regexp)
}
if answr.Replacement != "sip:1\\@172.16.1.10." {
t.Errorf("received: <%q>", answr.Replacement)
}
}
}

View File

@@ -31,7 +31,7 @@ import (
const (
QueryType = "QueryType"
E164Address = "E164"
E164Address = "E164Address"
)
// e164FromNAPTR extracts the E164 address out of a NAPTR name record
@@ -129,6 +129,16 @@ func appendDNSAnswer(msg *dns.Msg) (err error) {
Ttl: 60},
},
)
case dns.TypeNAPTR:
msg.Answer = append(msg.Answer,
&dns.NAPTR{
Hdr: dns.RR_Header{
Name: msg.Question[0].Name,
Rrtype: msg.Question[0].Qtype,
Class: dns.ClassINET,
Ttl: 60},
},
)
default:
return fmt.Errorf("unsupported DNS type: <%v>", msg.Question[0].Qtype)
}
@@ -146,7 +156,7 @@ func updateDNSMsgFromNM(msg *dns.Msg, nm *config.NavigableMap) (err error) {
continue
}
cfgItm := nmItms[0] // first item gives some config for the rest
if cfgItm.Config.NewBranch {
if len(msg.Answer) == 0 || cfgItm.Config.NewBranch {
if err = appendDNSAnswer(msg); err != nil {
return
}
@@ -155,31 +165,68 @@ func updateDNSMsgFromNM(msg *dns.Msg, nm *config.NavigableMap) (err error) {
return errors.New("empty path in config item")
}
switch cfgItm.Path[0] {
case utils.MetaResponseCode:
case utils.Rcode:
var itm int64
if itm, err = utils.IfaceAsInt64(cfgItm.Data); err != nil {
return fmt.Errorf("item: <%s>, err: %s", cfgItm.Path[0], err.Error())
}
msg.Rcode = int(itm)
continue
case utils.MetaAnswer:
lastAnswer := msg.Answer[len(msg.Answer)-1]
switch msg.Question[0].Qtype {
case dns.TypeA:
var ip string
if ip, err = utils.IfaceAsString(cfgItm.Data); err != nil {
return
}
lastAnswer.(*dns.A).A = net.ParseIP(ip)
case dns.TypeNAPTR:
if rr, err := dns.NewRR(`IN NAPTR 100 10 "U" "E2U+sip" "!^.*$!sip:customer-service@example.com!" .`); err != nil {
return err
} else {
lastAnswer = rr
}
case utils.Order:
if msg.Question[0].Qtype != dns.TypeNAPTR {
return fmt.Errorf("field <%s> only works with NAPTR", utils.Order)
}
var itm int64
if itm, err = utils.IfaceAsInt64(cfgItm.Data); err != nil {
return fmt.Errorf("item: <%s>, err: %s", cfgItm.Path[0], err.Error())
}
msg.Answer[len(msg.Answer)-1].(*dns.NAPTR).Order = uint16(itm)
case utils.Preference:
if msg.Question[0].Qtype != dns.TypeNAPTR {
return fmt.Errorf("field <%s> only works with NAPTR", utils.Preference)
}
var itm int64
if itm, err = utils.IfaceAsInt64(cfgItm.Data); err != nil {
return fmt.Errorf("item: <%s>, err: %s", cfgItm.Path[0], err.Error())
}
msg.Answer[len(msg.Answer)-1].(*dns.NAPTR).Preference = uint16(itm)
case utils.Flags:
if msg.Question[0].Qtype != dns.TypeNAPTR {
return fmt.Errorf("field <%s> only works with NAPTR", utils.Flags)
}
var itm string
if itm, err = utils.IfaceAsString(cfgItm.Data); err != nil {
return fmt.Errorf("item: <%s>, err: %s", cfgItm.Path[0], err.Error())
}
msg.Answer[len(msg.Answer)-1].(*dns.NAPTR).Flags = itm
case utils.Service:
if msg.Question[0].Qtype != dns.TypeNAPTR {
return fmt.Errorf("field <%s> only works with NAPTR", utils.Service)
}
var itm string
if itm, err = utils.IfaceAsString(cfgItm.Data); err != nil {
return fmt.Errorf("item: <%s>, err: %s", cfgItm.Path[0], err.Error())
}
msg.Answer[len(msg.Answer)-1].(*dns.NAPTR).Service = itm
case utils.Regexp:
if msg.Question[0].Qtype != dns.TypeNAPTR {
return fmt.Errorf("field <%s> only works with NAPTR", utils.Regexp)
}
var itm string
if itm, err = utils.IfaceAsString(cfgItm.Data); err != nil {
return fmt.Errorf("item: <%s>, err: %s", cfgItm.Path[0], err.Error())
}
msg.Answer[len(msg.Answer)-1].(*dns.NAPTR).Regexp = itm
case utils.Replacement:
if msg.Question[0].Qtype != dns.TypeNAPTR {
return fmt.Errorf("field <%s> only works with NAPTR", utils.Replacement)
}
var rplc string
if rplc, err = utils.IfaceAsString(cfgItm.Data); err != nil {
return fmt.Errorf("item: <%s>, err: %s", cfgItm.Path[0], err.Error())
}
msg.Answer[len(msg.Answer)-1].(*dns.NAPTR).Replacement = rplc
}
}
return
}

View File

@@ -11,10 +11,19 @@
"db_password": "CGRateS.org",
},
"scheduler": {
"enabled": true,
"cdrs_conns": [
{"address": "*internal"},
],
},
"sessions": {
"enabled": true,
"attributes_conns": [
{"address": "127.0.0.1:2012", "transport": "*json"}
{"address": "*internal"}
],
"rals_conns": [
{"address": "*internal"}

View File

@@ -1,6 +1,6 @@
{
"diameter_agent": {
"dns_agent": {
"request_processors": [
{
"id": "dryrun1",
@@ -10,6 +10,12 @@
{"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*sms"},
],
"reply_fields":[
{"tag": "NAPTROrder", "field_id": "Order", "type": "*constant", "value": "100"},
{"tag": "NAPTRPreference", "field_id": "Preference", "type": "*constant", "value": "10"},
{"tag": "NAPTRFlags", "field_id": "Flags", "type": "*constant", "value": "U"},
{"tag": "NAPTRService", "field_id": "Service", "type": "*constant", "value": "E2U+SIP"},
{"tag": "NAPTRRegexp", "field_id": "Regexp", "type": "*constant", "value": "^.*$"},
{"tag": "NAPTRReplacement", "field_id": "Replacement", "type": "*constant", "value": "sip:\\1@172.16.1.10."},
],
},
],

View File

@@ -571,9 +571,13 @@ const (
MetaApiKey = "*api_key"
UsageID = "UsageID"
Status = "status"
MetaResponseCode = "*response_code"
MetaAnswer = "*answer"
MetaTTL = "*ttl"
Rcode = "Rcode"
Replacement = "Replacement"
Regexp = "Regexp"
Order = "Order"
Preference = "Preference"
Flags = "Flags"
Service = "Service"
)
// Migrator Action