diff --git a/agents/astagent.go b/agents/astagent.go index ab2ff1124..8329a4715 100644 --- a/agents/astagent.go +++ b/agents/astagent.go @@ -364,3 +364,8 @@ func (sma *AsteriskAgent) V1GetActiveSessionIDs(ignParam string, return nil } + +// V1SendRAR is used to implement the sessions.BiRPClient interface +func (*AsteriskAgent) V1SendRAR(originID string, reply *string) (err error) { + return utils.ErrNotImplemented +} diff --git a/agents/diamagent.go b/agents/diamagent.go index 6d8bc055a..4a6ee3460 100644 --- a/agents/diamagent.go +++ b/agents/diamagent.go @@ -168,7 +168,8 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { return } // cache message for ASR - if da.cgrCfg.DiameterAgentCfg().ASRTemplate != "" { + if da.cgrCfg.DiameterAgentCfg().ASRTemplate != "" || + da.cgrCfg.DiameterAgentCfg().RARTemplate != "" { sessID, err := diamDP.FieldAsString([]string{"Session-Id"}) if err != nil { utils.Logger.Warning( @@ -419,7 +420,7 @@ func (da *DiameterAgent) processRequest(reqProcessor *config.RequestProcessor, return true, nil } -// rpcclient.ClientConnector interface +// Call implements rpcclient.ClientConnector interface func (da *DiameterAgent) Call(serviceMethod string, args interface{}, reply interface{}) error { return utils.RPCCall(da, serviceMethod, args, reply) } @@ -476,3 +477,49 @@ func (da *DiameterAgent) V1GetActiveSessionIDs(ignParam string, sessionIDs *[]*sessions.SessionID) error { return utils.ErrNotImplemented } + +// V1SendRAR sends a rar meseage to diameter client +func (da *DiameterAgent) V1SendRAR(originID string, reply *string) (err error) { + if originID == "" { + utils.Logger.Info( + fmt.Sprintf("<%s> cannot send RAR, missing session ID", + utils.DiameterAgent)) + return utils.ErrMandatoryIeMissing + } + msg, has := engine.Cache.Get(utils.CacheDiameterMessages, originID) + if !has { + utils.Logger.Warning( + fmt.Sprintf("<%s> cannot retrieve message from cache with OriginID: <%s>", + utils.DiameterAgent, originID)) + return utils.ErrMandatoryIeMissing + } + dmd := msg.(*diamMsgData) + aReq := NewAgentRequest( + newDADataProvider(dmd.c, dmd.m), + dmd.vars, + config.NewNavigableMap(nil), + config.NewNavigableMap(nil), + nil, + da.cgrCfg.GeneralCfg().DefaultTenant, + da.cgrCfg.GeneralCfg().DefaultTimezone, da.filterS, nil, nil) + if err = aReq.SetFields(da.cgrCfg.DiameterAgentCfg().Templates[da.cgrCfg.DiameterAgentCfg().RARTemplate]); err != nil { + utils.Logger.Warning( + fmt.Sprintf("<%s> cannot send RAR with OriginID: <%s>, err: %s", + utils.DiameterAgent, originID, err.Error())) + return utils.ErrServerError + } + m := diam.NewRequest(dmd.m.Header.CommandCode, + dmd.m.Header.ApplicationID, dmd.m.Dictionary()) + if err = updateDiamMsgFromNavMap(m, aReq.diamreq, + da.cgrCfg.GeneralCfg().DefaultTimezone); err != nil { + utils.Logger.Warning( + fmt.Sprintf("<%s> cannot send RAR with OriginID: <%s>, err: %s", + utils.DiameterAgent, originID, err.Error())) + return utils.ErrServerError + } + if err = writeOnConn(dmd.c, m); err != nil { + return utils.ErrServerError + } + *reply = utils.OK + return +} diff --git a/agents/fsagent.go b/agents/fsagent.go index 6316278eb..6f0a67e0d 100644 --- a/agents/fsagent.go +++ b/agents/fsagent.go @@ -439,3 +439,8 @@ func (sm *FSsessions) Reload() { sm.conns = make([]*fsock.FSock, len(sm.cfg.EventSocketConns)) sm.senderPools = make([]*fsock.FSockPool, len(sm.cfg.EventSocketConns)) } + +// V1SendRAR is used to implement the sessions.BiRPClient interface +func (*FSsessions) V1SendRAR(originID string, reply *string) (err error) { + return utils.ErrNotImplemented +} diff --git a/agents/kamagent.go b/agents/kamagent.go index 639791b9d..f7627ec81 100644 --- a/agents/kamagent.go +++ b/agents/kamagent.go @@ -423,3 +423,8 @@ func (ka *KamailioAgent) V1GetActiveSessionIDs(ignParam string, sessionIDs *[]*s func (ka *KamailioAgent) Reload() { ka.conns = make([]*kamevapi.KamEvapi, len(ka.cfg.EvapiConns)) } + +// V1SendRAR is used to implement the sessions.BiRPClient interface +func (*KamailioAgent) V1SendRAR(originID string, reply *string) (err error) { + return utils.ErrNotImplemented +} diff --git a/apier/v1/sessions.go b/apier/v1/sessions.go index dcd2bda9f..a456fe734 100644 --- a/apier/v1/sessions.go +++ b/apier/v1/sessions.go @@ -141,3 +141,8 @@ func (ssv1 *SessionSv1) Call(serviceMethod string, args interface{}, reply interface{}) error { return utils.APIerRPCCall(ssv1, serviceMethod, args, reply) } + +// SendRAR sends the RAR for filterd sessions +func (smgv1 *SessionSv1) SendRAR(args *utils.SessionFilter, reply *string) error { + return smgv1.Ss.BiRPCv1SendRAR(nil, args, reply) +} diff --git a/apier/v1/sessionsbirpc.go b/apier/v1/sessionsbirpc.go index be4317d8c..fb44b55b9 100644 --- a/apier/v1/sessionsbirpc.go +++ b/apier/v1/sessionsbirpc.go @@ -52,6 +52,8 @@ func (ssv1 *SessionSv1) Handlers() map[string]interface{} { utils.SessionSv1SetPassiveSession: ssv1.BiRPCv1SetPassiveSession, utils.SessionSv1ActivateSessions: ssv1.BiRPCv1ActivateSessions, utils.SessionSv1DeactivateSessions: ssv1.BiRPCv1DeactivateSessions, + + utils.SessionSv1SendRAR: ssv1.BiRPCV1SendRAR, } } @@ -162,3 +164,9 @@ func (ssv1 *SessionSv1) BiRPCv1DeactivateSessions(clnt *rpc2.Client, args *utils.SessionIDsWithArgsDispatcher, reply *string) error { return ssv1.Ss.BiRPCv1DeactivateSessions(clnt, args, reply) } + +// BiRPCV1SendRAR sends the RAR for filterd sessions +func (ssv1 *SessionSv1) BiRPCV1SendRAR(clnt *rpc2.Client, + args *utils.SessionFilter, reply *string) error { + return ssv1.Ss.BiRPCv1SendRAR(clnt, args, reply) +} diff --git a/config/config_defaults.go b/config/config_defaults.go index 7e078adb6..00c502993 100755 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -415,6 +415,7 @@ const CGRATES_CFG_JSON = ` "concurrent_requests": -1, // limit the number of active requests processed by the server <-1|0-n> "synced_conn_requests": false, // process one request at the time per connection "asr_template": "", // enable AbortSession message being sent to client on DisconnectSession + "rar_template": "", // for building the RAR "templates":{ // default message templates "*err": [ {"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable", @@ -457,6 +458,26 @@ const CGRATES_CFG_JSON = ` "value": "~*req.User-Name", "mandatory": true}, {"tag": "OriginStateID", "path": "*diamreq.Origin-State-Id", "type": "*constant", "value": "1"}, + ], + "*rar": [ + {"tag": "SessionId", "path": "*diamreq.Session-Id", "type": "*variable", + "value": "~*req.Session-Id", "mandatory": true}, + {"tag": "OriginHost", "path": "*diamreq.Origin-Host", "type": "*variable", + "value": "~*req.Destination-Host", "mandatory": true}, + {"tag": "OriginRealm", "path": "*diamreq.Origin-Realm", "type": "*variable", + "value": "~*req.Destination-Realm", "mandatory": true}, + {"tag": "DestinationRealm", "path": "*diamreq.Destination-Realm", "type": "*variable", + "value": "~*req.Origin-Realm", "mandatory": true}, + {"tag": "DestinationHost", "path": "*diamreq.Destination-Host", "type": "*variable", + "value": "~*req.Origin-Host", "mandatory": true}, + {"tag": "AuthApplicationId", "path": "*diamreq.Auth-Application-Id", "type": "*variable", + "value": "~*vars.*appid", "mandatory": true}, + {"tag": "UserName", "path": "*diamreq.User-Name", "type": "*variable", + "value": "~*req.User-Name", "mandatory": true}, + {"tag": "OriginStateID", "path": "*diamreq.Origin-State-Id", "type": "*constant", + "value": "1"}, + {"tag": "ReAuthRequestType", "path": "*diamreq.Re-Auth-Request-Type", "type": "*constant", + "value": "0"}, ] }, "request_processors": [ // list of processors to be applied to diameter messages diff --git a/config/config_json_test.go b/config/config_json_test.go index bc4ea96c3..73d6c81d6 100755 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -756,6 +756,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { Concurrent_requests: utils.IntPointer(-1), Synced_conn_requests: utils.BoolPointer(false), Asr_template: utils.StringPointer(""), + Rar_template: utils.StringPointer(""), Templates: map[string][]*FcTemplateJsonCfg{ utils.MetaErr: { { @@ -869,13 +870,67 @@ func TestDiameterAgentJsonCfg(t *testing.T) { Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("1")}, }, + utils.MetaRAR: { + { + Tag: utils.StringPointer("SessionId"), + Path: utils.StringPointer(fmt.Sprintf("%s.Session-Id", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.Session-Id"), + Mandatory: utils.BoolPointer(true)}, + { + Tag: utils.StringPointer("OriginHost"), + Path: utils.StringPointer(fmt.Sprintf("%s.Origin-Host", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.Destination-Host"), + Mandatory: utils.BoolPointer(true)}, + { + Tag: utils.StringPointer("OriginRealm"), + Path: utils.StringPointer(fmt.Sprintf("%s.Origin-Realm", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.Destination-Realm"), + Mandatory: utils.BoolPointer(true)}, + { + Tag: utils.StringPointer("DestinationRealm"), + Path: utils.StringPointer(fmt.Sprintf("%s.Destination-Realm", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.Origin-Realm"), + Mandatory: utils.BoolPointer(true)}, + { + Tag: utils.StringPointer("DestinationHost"), + Path: utils.StringPointer(fmt.Sprintf("%s.Destination-Host", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.Origin-Host"), + Mandatory: utils.BoolPointer(true)}, + { + Tag: utils.StringPointer("AuthApplicationId"), + Path: utils.StringPointer(fmt.Sprintf("%s.Auth-Application-Id", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*vars.*appid"), + Mandatory: utils.BoolPointer(true)}, + { + Tag: utils.StringPointer("UserName"), + Path: utils.StringPointer(fmt.Sprintf("%s.User-Name", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.User-Name"), + Mandatory: utils.BoolPointer(true)}, + { + Tag: utils.StringPointer("OriginStateID"), + Path: utils.StringPointer(fmt.Sprintf("%s.Origin-State-Id", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.META_CONSTANT), + Value: utils.StringPointer("1")}, + { + Tag: utils.StringPointer("ReAuthRequestType"), + Path: utils.StringPointer(fmt.Sprintf("%s.Re-Auth-Request-Type", utils.MetaDiamreq)), + Type: utils.StringPointer(utils.META_CONSTANT), + Value: utils.StringPointer("0")}, + }, }, Request_processors: &[]*ReqProcessorJsnCfg{}, } if cfg, err := dfCgrJsonCfg.DiameterAgentJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { - t.Errorf("expecting: %s, \n\nreceived: %s", utils.ToIJSON(eCfg), utils.ToIJSON(cfg)) + t.Errorf("expecting: %s, \n\nreceived: %s", utils.ToJSON(eCfg), utils.ToJSON(cfg)) } } diff --git a/config/diametercfg.go b/config/diametercfg.go index 7cc09dfbb..3c9618b9b 100644 --- a/config/diametercfg.go +++ b/config/diametercfg.go @@ -33,6 +33,7 @@ type DiameterAgentCfg struct { ConcurrentReqs int // limit the maximum number of requests processed SyncedConnReqs bool ASRTemplate string + RARTemplate string Templates map[string][]*FCTemplate RequestProcessors []*RequestProcessor } @@ -85,6 +86,9 @@ func (da *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg, separa if jsnCfg.Asr_template != nil { da.ASRTemplate = *jsnCfg.Asr_template } + if jsnCfg.Rar_template != nil { + da.RARTemplate = *jsnCfg.Rar_template + } if jsnCfg.Templates != nil { if da.Templates == nil { da.Templates = make(map[string][]*FCTemplate) diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 8ec9b293c..ff25cb225 100755 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -313,6 +313,7 @@ type DiameterAgentJsonCfg struct { Concurrent_requests *int Synced_conn_requests *bool Asr_template *string + Rar_template *string Templates map[string][]*FcTemplateJsonCfg Request_processors *[]*ReqProcessorJsnCfg } diff --git a/sessions/libsessions.go b/sessions/libsessions.go index 21eb7c124..ced2368c0 100644 --- a/sessions/libsessions.go +++ b/sessions/libsessions.go @@ -44,6 +44,7 @@ type BiRPClient interface { Call(serviceMethod string, args interface{}, reply interface{}) error V1DisconnectSession(args utils.AttrDisconnectSession, reply *string) (err error) V1GetActiveSessionIDs(ignParam string, sessionIDs *[]*SessionID) (err error) + V1SendRAR(originID string, reply *string) (err error) } // getSessionTTL retrieves SessionTTL setting out of ev diff --git a/sessions/sessions.go b/sessions/sessions.go index 62ee64dde..b13c8f74f 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -3613,3 +3613,50 @@ func (sS *SessionS) BiRPCV1ProcessCDR(clnt rpcclient.ClientConnector, Event: ev}}, rply) } + +func (sS *SessionS) sendRar(s *Session) (err error) { + clnt := sS.biJClnt(s.ClientConnID) + if clnt == nil { + return fmt.Errorf("calling %s requires bidirectional JSON connection, connID: <%s>", + utils.SessionSv1SendRAR, s.ClientConnID) + } + var originID string + if originID, err = s.EventStart.GetString(utils.OriginID); err != nil { + return + } + var rply string + if err = clnt.conn.Call(utils.SessionSv1SendRAR, originID, &rply); err == utils.ErrNotImplemented { + err = nil + } + return +} + +// BiRPCv1SendRAR sends a RAR for sessions matching sessions +func (sS *SessionS) BiRPCv1SendRAR(clnt rpcclient.ClientConnector, + args *utils.SessionFilter, reply *string) (err error) { + if args == nil { //protection in case on nil + args = &utils.SessionFilter{} + } + aSs := sS.filterSessions(args, false) + if len(aSs) == 0 { + return utils.ErrNotFound + } + for _, as := range aSs { + ss := sS.getSessions(as.CGRID, false) + if len(ss) == 0 { + continue + } + if errTerm := sS.sendRar(ss[0]); errTerm != nil { + utils.Logger.Warning( + fmt.Sprintf( + "<%s> failed sending RAR for session with id: <%s>, err: <%s>", + utils.SessionS, ss[0].cgrID(), errTerm.Error())) + err = utils.ErrPartiallyExecuted + } + } + if err != nil { + return + } + *reply = utils.OK + return +} diff --git a/utils/consts.go b/utils/consts.go index aac8aab8a..ace166983 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -750,6 +750,7 @@ const ( MetaSum = "*sum" MetaAverage = "*average" MetaDistinct = "*distinct" + MetaRAR = "*rar" ) // Services @@ -1355,6 +1356,7 @@ const ( SessionSv1ActivateSessions = "SessionSv1.ActivateSessions" SessionSv1DeactivateSessions = "SessionSv1.DeactivateSessions" SMGenericV1InitiateSession = "SMGenericV1.InitiateSession" + SessionSv1SendRAR = "SessionSv1.SendRAR" ) // Responder APIs