diff --git a/agents/dnsagent.go b/agents/dnsagent.go
index 2f5d97c32..a457a9186 100644
--- a/agents/dnsagent.go
+++ b/agents/dnsagent.go
@@ -17,3 +17,57 @@ along with this program. If not, see
*/
package agents
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/cgrates/cgrates/config"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+ "github.com/cgrates/rpcclient"
+ "github.com/miekg/dns"
+)
+
+// NewDNSAgent is the constructor for DNSAgent
+func NewDNSAgent(cgrCfg *config.CGRConfig, fltrS *engine.FilterS,
+ sS rpcclient.RpcClientConnection) (da *DNSAgent, err error) {
+ da = &DNSAgent{cgrCfg: cgrCfg, fltrS: fltrS, sS: sS}
+ return
+}
+
+// DNSAgent translates DNS requests towards CGRateS infrastructure
+type DNSAgent struct {
+ cgrCfg *config.CGRConfig // loaded CGRateS configuration
+ fltrS *engine.FilterS // connection towards FilterS
+ sS rpcclient.RpcClientConnection // connection towards CGR-SessionS component
+}
+
+// ListenAndServe will run the DNS handler doing also the connection to listen address
+func (da *DNSAgent) ListenAndServe() error {
+ if strings.HasSuffix(da.cgrCfg.DNSAgentCfg().ListenNet, utils.TLSNoCaps) {
+ return dns.ListenAndServeTLS(
+ da.cgrCfg.DNSAgentCfg().Listen,
+ da.cgrCfg.TlsCfg().ServerCerificate,
+ da.cgrCfg.TlsCfg().ServerKey,
+ dns.HandlerFunc(
+ func(w ResponseWriter, m *Msg) {
+ go da.handleMessage(w, m)
+ }),
+ )
+ }
+ return dns.ListenAndServe(
+ da.cgrCfg.DNSAgentCfg().Listen,
+ da.cgrCfg.DNSAgentCfg().ListenNet,
+ dns.HandlerFunc(
+ func(w ResponseWriter, m *Msg) {
+ go da.handleMessage(w, m)
+ }),
+ )
+}
+
+// handleMessage is the entry point of all DNS requests
+// requests are reaching here asynchronously
+func (da *DNSAgent) handleMessage(w ResponseWriter, m *dns.Msg) {
+ fmt.Printf("got message: %+v\n", m)
+}
diff --git a/config/config.go b/config/config.go
index eb7d4800b..79465a6b3 100755
--- a/config/config.go
+++ b/config/config.go
@@ -152,6 +152,7 @@ func NewDefaultCGRConfig() (*CGRConfig, error) {
cfg.asteriskAgentCfg = new(AsteriskAgentCfg)
cfg.diameterAgentCfg = new(DiameterAgentCfg)
cfg.radiusAgentCfg = new(RadiusAgentCfg)
+ cfg.dnsAgentCfg = new(DNSAgentCfg)
cfg.attributeSCfg = new(AttributeSCfg)
cfg.chargerSCfg = new(ChargerSCfg)
cfg.resourceSCfg = new(ResourceSConfig)
@@ -324,6 +325,7 @@ type CGRConfig struct {
asteriskAgentCfg *AsteriskAgentCfg // AsteriskAgent config
diameterAgentCfg *DiameterAgentCfg // DiameterAgent config
radiusAgentCfg *RadiusAgentCfg // RadiusAgent config
+ dnsAgentCfg *DNSAgentCfg // DNSAgent config
attributeSCfg *AttributeSCfg // AttributeS config
chargerSCfg *ChargerSCfg // ChargerS config
resourceSCfg *ResourceSConfig // ResourceS config
@@ -605,6 +607,13 @@ func (self *CGRConfig) checkConfigSanity() error {
}
}
}
+ if self.dnsAgentCfg.Enabled && !self.sessionSCfg.Enabled {
+ for _, sSConn := range self.dnsAgentCfg.SessionSConns {
+ if sSConn.Address == utils.MetaInternal {
+ return fmt.Errorf("%s not enabled but referenced by %s", utils.SessionS, utils.DNSAgent)
+ }
+ }
+ }
// HTTPAgent checks
for _, httpAgentCfg := range self.httpAgentCfg {
// httpAgent checks
@@ -840,6 +849,14 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) {
return err
}
+ jsnDNSCfg, err := jsnCfg.DNSAgentJsonCfg()
+ if err != nil {
+ return err
+ }
+ if err := self.dnsAgentCfg.loadFromJsonCfg(jsnDNSCfg, self.generalCfg.RsrSepatarot); err != nil {
+ return err
+ }
+
jsnHttpAgntCfg, err := jsnCfg.HttpAgentJsonCfg()
if err != nil {
return err
@@ -1050,6 +1067,10 @@ func (self *CGRConfig) RadiusAgentCfg() *RadiusAgentCfg {
return self.radiusAgentCfg
}
+func (self *CGRConfig) DNSAgentCfg() *DNSAgentCfg {
+ return self.dnsAgentCfg
+}
+
func (cfg *CGRConfig) AttributeSCfg() *AttributeSCfg {
return cfg.attributeSCfg
}
diff --git a/config/config_defaults.go b/config/config_defaults.go
index 20215a3ae..127c1d471 100755
--- a/config/config_defaults.go
+++ b/config/config_defaults.go
@@ -453,6 +453,19 @@ const CGRATES_CFG_JSON = `
],
+"dns_agent": {
+ "enabled": false, // enables the DNS agent:
+ "listen_net": "udp", // network to listen on
+ "listen": "127.0.0.1:53", // address where to listen for DNS requests
+ "sessions_conns": [ // connections to SessionS for session management and CDR posting
+ {"address": "*internal"}
+ ],
+ "timezone": "", // timezone of the events if not specified
+ "request_processors": [ // request processors to be applied to DNS messages
+ ],
+},
+
+
"attributes": { // AttributeS config
"enabled": false, // starts attribute service: .
"indexed_selects":true, // enable profile matching exclusively on indexes
diff --git a/config/config_json.go b/config/config_json.go
index f4039e71a..280dd0dd4 100644
--- a/config/config_json.go
+++ b/config/config_json.go
@@ -66,6 +66,7 @@ const (
TlsCfgJson = "tls"
AnalyzerCfgJson = "analyzers"
Apier = "apier"
+ DNSAgentJson = "dns_agent"
)
// Loads the json config out of io.Reader, eg other sources than file, maybe over http
@@ -320,6 +321,16 @@ func (self CgrJsonCfg) HttpAgentJsonCfg() (*[]*HttpAgentJsonCfg, error) {
return &httpAgnt, nil
}
+func (self CgrJsonCfg) DNSAgentJsonCfg() (da *DNSAgentJsonCfg, err error) {
+ rawCfg, hasKey := self[DNSAgentJson]
+ if !hasKey {
+ return
+ }
+ da = new(DNSAgentJsonCfg)
+ err = json.Unmarshal(*rawCfg, da)
+ return
+}
+
func (cgrJsn CgrJsonCfg) AttributeServJsonCfg() (*AttributeSJsonCfg, error) {
rawCfg, hasKey := cgrJsn[ATTRIBUTE_JSN]
if !hasKey {
diff --git a/config/config_json_test.go b/config/config_json_test.go
index f5b2bef36..48fe9daf2 100755
--- a/config/config_json_test.go
+++ b/config/config_json_test.go
@@ -737,6 +737,26 @@ func TestHttpAgentJsonCfg(t *testing.T) {
}
}
+func TestDNSAgentJsonCfg(t *testing.T) {
+ eCfg := &DNSAgentJsonCfg{
+ Enabled: utils.BoolPointer(false),
+ Listen_net: utils.StringPointer("udp"),
+ Listen: utils.StringPointer("127.0.0.1:53"),
+ Sessions_conns: &[]*HaPoolJsonCfg{
+ {
+ Address: utils.StringPointer(utils.MetaInternal),
+ }},
+ Timezone: utils.StringPointer(""),
+ Request_processors: &[]*ReqProcessorJsnCfg{},
+ }
+ if cfg, err := dfCgrJsonCfg.DNSAgentJsonCfg(); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(eCfg, cfg) {
+ rcv := *cfg.Request_processors
+ t.Errorf("Received: %+v", rcv)
+ }
+}
+
func TestDfAttributeServJsonCfg(t *testing.T) {
eCfg := &AttributeSJsonCfg{
Enabled: utils.BoolPointer(false),
diff --git a/config/dnsagntcfg.go b/config/dnsagntcfg.go
new file mode 100644
index 000000000..34de430cd
--- /dev/null
+++ b/config/dnsagntcfg.go
@@ -0,0 +1,122 @@
+/*
+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 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package config
+
+import (
+ "github.com/cgrates/cgrates/utils"
+)
+
+type DNSAgentCfg struct {
+ Enabled bool
+ ListenNet string // udp or tcp
+ Listen string
+ SessionSConns []*RemoteHost
+ Timezone string
+ RequestProcessors []*RequestProcessor
+}
+
+func (da *DNSAgentCfg) loadFromJsonCfg(jsnCfg *DNSAgentJsonCfg, sep string) (err error) {
+ if jsnCfg == nil {
+ return nil
+ }
+ if jsnCfg.Enabled != nil {
+ da.Enabled = *jsnCfg.Enabled
+ }
+ if jsnCfg.Listen_net != nil {
+ da.ListenNet = *jsnCfg.Listen_net
+ }
+ if jsnCfg.Listen != nil {
+ da.Listen = *jsnCfg.Listen
+ }
+ if jsnCfg.Sessions_conns != nil {
+ da.SessionSConns = make([]*RemoteHost, len(*jsnCfg.Sessions_conns))
+ for idx, jsnHaCfg := range *jsnCfg.Sessions_conns {
+ da.SessionSConns[idx] = NewDfltRemoteHost()
+ da.SessionSConns[idx].loadFromJsonCfg(jsnHaCfg)
+ }
+ }
+ if jsnCfg.Request_processors != nil {
+ for _, reqProcJsn := range *jsnCfg.Request_processors {
+ rp := new(RequestProcessor)
+ var haveID bool
+ for _, rpSet := range da.RequestProcessors {
+ if reqProcJsn.ID != nil && rpSet.ID == *reqProcJsn.ID {
+ rp = rpSet // Will load data into the one set
+ haveID = true
+ break
+ }
+ }
+ if err := rp.loadFromJsonCfg(reqProcJsn, sep); err != nil {
+ return nil
+ }
+ if !haveID {
+ da.RequestProcessors = append(da.RequestProcessors, rp)
+ }
+ }
+ }
+ return nil
+}
+
+// One request processor configuration
+type RequestProcessor struct {
+ ID string
+ Tenant RSRParsers
+ Filters []string
+ Flags utils.StringMap
+ ContinueOnSuccess bool
+ RequestFields []*FCTemplate
+ ReplyFields []*FCTemplate
+}
+
+func (rp *RequestProcessor) loadFromJsonCfg(jsnCfg *ReqProcessorJsnCfg, sep string) (err error) {
+ if jsnCfg == nil {
+ return nil
+ }
+ if jsnCfg.ID != nil {
+ rp.ID = *jsnCfg.ID
+ }
+ if jsnCfg.Filters != nil {
+ rp.Filters = make([]string, len(*jsnCfg.Filters))
+ for i, fltr := range *jsnCfg.Filters {
+ rp.Filters[i] = fltr
+ }
+ }
+ if jsnCfg.Flags != nil {
+ rp.Flags = utils.StringMapFromSlice(*jsnCfg.Flags)
+ }
+ if jsnCfg.Continue_on_success != nil {
+ rp.ContinueOnSuccess = *jsnCfg.Continue_on_success
+ }
+ if jsnCfg.Tenant != nil {
+ if rp.Tenant, err = NewRSRParsers(*jsnCfg.Tenant, true, sep); err != nil {
+ return err
+ }
+ }
+ if jsnCfg.Request_fields != nil {
+ if rp.RequestFields, err = FCTemplatesFromFCTemplatesJsonCfg(*jsnCfg.Request_fields, sep); err != nil {
+ return
+ }
+ }
+ if jsnCfg.Reply_fields != nil {
+ if rp.ReplyFields, err = FCTemplatesFromFCTemplatesJsonCfg(*jsnCfg.Reply_fields, sep); err != nil {
+ return
+ }
+ }
+ return nil
+}
diff --git a/config/libconfig_json.go b/config/libconfig_json.go
index ebf3704ca..77fc94683 100755
--- a/config/libconfig_json.go
+++ b/config/libconfig_json.go
@@ -360,7 +360,6 @@ type RadiusAgentJsonCfg struct {
Client_secrets *map[string]string
Client_dictionaries *map[string]string
Sessions_conns *[]*HaPoolJsonCfg
- Tenant *string
Timezone *string
Request_processors *[]*RAReqProcessorJsnCfg
}
@@ -397,11 +396,25 @@ type HttpAgentProcessorJsnCfg struct {
Reply_fields *[]*FcTemplateJsonCfg
}
-// History server config section
-type HistServJsonCfg struct {
- Enabled *bool
- History_dir *string
- Save_interval *string
+// DNSAgentJsonCfg
+type DNSAgentJsonCfg struct {
+ Enabled *bool
+ Listen *string
+ Listen_net *string
+ Sessions_conns *[]*HaPoolJsonCfg
+ Timezone *string
+ Request_processors *[]*ReqProcessorJsnCfg
+}
+
+type ReqProcessorJsnCfg struct {
+ ID *string
+ Filters *[]string
+ Tenant *string
+ Timezone *string
+ Flags *[]string
+ Continue_on_success *bool
+ Request_fields *[]*FcTemplateJsonCfg
+ Reply_fields *[]*FcTemplateJsonCfg
}
// Attribute service config section
diff --git a/utils/consts.go b/utils/consts.go
index 7b105d629..4a092bea8 100755
--- a/utils/consts.go
+++ b/utils/consts.go
@@ -562,6 +562,8 @@ const (
MetaRemove = "*remove"
MetaClear = "*clear"
LoadIDs = "load_ids"
+ DNSAgent = "DNSAgent"
+ TLSNoCaps = "tls"
)
// Migrator Action