From f6a9ec0ab6f88bec494dfc77cbba155837d9d744 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 26 Apr 2019 15:59:02 +0200 Subject: [PATCH] DNSAgent - basic ENUM implementation working with dryRun --- agents/dnsagent.go | 38 ++++------- agents/dnsagent_it_test.go | 42 ++++++------ agents/libdns.go | 87 +++++++++++++++++++------ data/conf/samples/dnsagent/cgrates.json | 11 +++- data/conf/samples/dnsagent/dryrun.json | 8 ++- utils/consts.go | 10 ++- 6 files changed, 128 insertions(+), 68 deletions(-) diff --git a/agents/dnsagent.go b/agents/dnsagent.go index 93ed7818d..ae9865df3 100644 --- a/agents/dnsagent.go +++ b/agents/dnsagent.go @@ -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, diff --git a/agents/dnsagent_it_test.go b/agents/dnsagent_it_test.go index c0a5bcdf1..838916f7e 100644 --- a/agents/dnsagent_it_test.go +++ b/agents/dnsagent_it_test.go @@ -21,7 +21,6 @@ along with this program. If not, see 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) + } } } diff --git a/agents/libdns.go b/agents/libdns.go index e08307ad2..e4def101d 100644 --- a/agents/libdns.go +++ b/agents/libdns.go @@ -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 } diff --git a/data/conf/samples/dnsagent/cgrates.json b/data/conf/samples/dnsagent/cgrates.json index 78b4c8b1e..3f4be3ba6 100644 --- a/data/conf/samples/dnsagent/cgrates.json +++ b/data/conf/samples/dnsagent/cgrates.json @@ -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"} diff --git a/data/conf/samples/dnsagent/dryrun.json b/data/conf/samples/dnsagent/dryrun.json index 661bc260c..bcf991215 100644 --- a/data/conf/samples/dnsagent/dryrun.json +++ b/data/conf/samples/dnsagent/dryrun.json @@ -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."}, ], }, ], diff --git a/utils/consts.go b/utils/consts.go index 311762481..c552f74d7 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -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