1 /*
2  * Copyright (C) 2011 Stefan Sayer
3  * Copyright (C) 2012 FRAFOS GmbH
4  * Copyright (C) 2014 Stefan Sayer
5  *
6  * This file is part of SEMS, a free SIP media server.
7  *
8  * SEMS is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include "AmPlugIn.h"
29 #include "log.h"
30 #include "AmArg.h"
31 #include "RegisterCache.h"
32 #include "AmUriParser.h"
33 
34 #include "Registrar.h"
35 
36 #include "SBCCallControlAPI.h"
37 
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <pwd.h>
41 #include <string.h>
42 #include <fstream>
43 
44 using namespace std;
45 
46 class CCRegistrarFactory : public AmDynInvokeFactory
47 {
48 public:
CCRegistrarFactory(const string & name)49     CCRegistrarFactory(const string& name)
50 	: AmDynInvokeFactory(name) {}
51 
getInstance()52     AmDynInvoke* getInstance(){
53 	return CCRegistrar::instance();
54     }
55 
onLoad()56     int onLoad(){
57       if (CCRegistrar::instance()->onLoad())
58 	return -1;
59 
60       DBG("template call control loaded.\n");
61 
62       return 0;
63     }
64 };
65 
66 EXPORT_PLUGIN_CLASS_FACTORY(CCRegistrarFactory, MOD_NAME);
67 
68 CCRegistrar* CCRegistrar::_instance=0;
69 
instance()70 CCRegistrar* CCRegistrar::instance()
71 {
72     if(!_instance)
73 	_instance = new CCRegistrar();
74     return _instance;
75 }
76 
CCRegistrar()77 CCRegistrar::CCRegistrar()
78 {
79 }
80 
~CCRegistrar()81 CCRegistrar::~CCRegistrar() { }
82 
onLoad()83 int CCRegistrar::onLoad() {
84   AmConfigReader cfg;
85 
86   // if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
87   //   INFO(MOD_NAME "configuration  file (%s) not found, "
88   // 	 "assuming default configuration is fine\n",
89   // 	 (AmConfig::ModConfigPath + string(MOD_NAME ".conf")).c_str());
90   //   return 0;
91   // }
92 
93   // syslog_prefix = cfg.hasParameter("cdr_prefix") ?
94   //   cfg.getParameter("cdr_prefix") : syslog_prefix;
95 
96   return 0;
97 }
98 
invoke(const string & method,const AmArg & args,AmArg & ret)99 void CCRegistrar::invoke(const string& method, const AmArg& args, AmArg& ret)
100 {
101   DBG("CCRegistrar: %s(%s)\n", method.c_str(), AmArg::print(args).c_str());
102 
103   if(method == "start"){
104 
105     SBCCallProfile* call_profile =
106       dynamic_cast<SBCCallProfile*>(args[CC_API_PARAMS_CALL_PROFILE].asObject());
107 
108     const AmSipRequest* msg = dynamic_cast<const AmSipRequest*>(args[CC_API_PARAMS_SIP_MSG].asObject());
109 
110     start(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(),
111 	  args[CC_API_PARAMS_LTAG].asCStr(),
112 	  call_profile,
113 	  args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_START_SEC].asInt(),
114 	  args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_START_USEC].asInt(),
115 	  args[CC_API_PARAMS_CFGVALUES],
116 	  args[CC_API_PARAMS_TIMERID].asInt(),  ret, msg);
117 
118   } else if(method == "connect"){
119 
120     SBCCallProfile* call_profile =
121       dynamic_cast<SBCCallProfile*>(args[CC_API_PARAMS_CALL_PROFILE].asObject());
122 
123     connect(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(),
124 	    args[CC_API_PARAMS_LTAG].asCStr(),
125 	    call_profile,
126 	    args[CC_API_PARAMS_OTHERID].asCStr(),
127 	    args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_CONNECT_SEC].asInt(),
128 	    args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_CONNECT_USEC].asInt());
129 
130   } else if(method == "end"){
131 
132     SBCCallProfile* call_profile =
133       dynamic_cast<SBCCallProfile*>(args[CC_API_PARAMS_CALL_PROFILE].asObject());
134 
135     end(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(),
136 	args[CC_API_PARAMS_LTAG].asCStr(),
137 	call_profile,
138 	args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_END_SEC].asInt(),
139 	args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_END_USEC].asInt()
140 	);
141   } else if (method == "route"){
142     SBCCallProfile* call_profile =
143 
144       dynamic_cast<SBCCallProfile*>(args[CC_API_PARAMS_CALL_PROFILE].asObject());
145 
146     const AmSipRequest* msg = dynamic_cast<const AmSipRequest*>(args[CC_API_PARAMS_SIP_MSG].asObject());
147 
148     route(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(), call_profile, msg,
149 	  args[CC_API_PARAMS_CFGVALUES], ret);
150 
151   } else if(method == "_list"){
152     ret.push("start");
153     ret.push("connect");
154     ret.push("end");
155     ret.push("route");
156   }
157   else
158     throw AmDynInvoke::NotImplemented(method);
159 }
160 
retarget(const string & r_uri,const AmArg & values,SBCCallProfile * call_profile)161 bool retarget(const string& r_uri, const AmArg& values, SBCCallProfile* call_profile) {
162   bool nat_handling = true; // default
163   bool sticky_iface = true; // default
164   if (values.hasMember("nat_handling") && string(values["nat_handling"].asCStr())=="no") {
165     nat_handling = false;
166   }
167   if (values.hasMember("sticky_iface") && string(values["sticky_iface"].asCStr())=="no") {
168     sticky_iface = false;
169   }
170   DBG("Registrar handling message to R-URI '%s'\n", r_uri.c_str());
171   DBG("nat_handling: %sabled, sticky_iface: %sabled\n", nat_handling?"en":"dis", sticky_iface?"en":"dis");
172 
173   // should we check if the R-URI has already been changed?
174   string aor = RegisterCache::canonicalize_aor(r_uri);
175   RegisterCache* reg_cache = RegisterCache::instance();
176 
177   map<string,string> alias_map;
178   if(!aor.empty() && reg_cache->getAorAliasMap(aor,alias_map) &&
179      !alias_map.empty()) {
180 
181     AliasEntry alias_entry;
182     const string& alias = alias_map.begin()->first; // no forking
183     if(!alias.empty() && reg_cache->findAliasEntry(alias,alias_entry)) {
184 
185       call_profile->ruri = alias_entry.contact_uri;
186       DBG("R-URI from location DB: '%s'\n", alias_entry.contact_uri.c_str());
187 
188       if (nat_handling) {
189 	call_profile->next_hop = alias_entry.source_ip;
190 	if(alias_entry.source_port != 5060)
191 	  call_profile->next_hop += ":" + int2str(alias_entry.source_port);
192 	call_profile->next_hop += "/" + alias_entry.trsp;
193       }
194 
195       if (sticky_iface) {
196 	string out_if = AmConfig::SIP_Ifs[alias_entry.local_if].name;
197 	DBG("out_if = '%s'",out_if.c_str());
198 
199 	call_profile->outbound_interface = out_if;
200 	DBG("setting from registration cache: outbound_interface='%s'\n",
201 	    call_profile->outbound_interface.c_str());
202       }
203 
204       return true;
205     }
206   }
207 
208   DBG("No registration found for r-ruri '%s'/ aor '%s' in location database.\n",
209       r_uri.c_str(), aor.c_str());
210 
211   return false;
212 }
213 
214 #define REFUSE_WITH_404					\
215   {							\
216   res.push(AmArg());					\
217   AmArg& res_cmd = res.back();				\
218   res_cmd[SBC_CC_ACTION] = SBC_CC_REFUSE_ACTION;	\
219   res_cmd[SBC_CC_REFUSE_CODE] = (int)404;		\
220   res_cmd[SBC_CC_REFUSE_REASON] = "Not Found";		\
221   }
222 
223 
start(const string & cc_name,const string & ltag,SBCCallProfile * call_profile,int start_ts_sec,int start_ts_usec,const AmArg & values,int timer_id,AmArg & res,const AmSipRequest * req)224 void CCRegistrar::start(const string& cc_name, const string& ltag,
225 		       SBCCallProfile* call_profile,
226 		       int start_ts_sec, int start_ts_usec,
227 		       const AmArg& values, int timer_id, AmArg& res, const AmSipRequest* req) {
228   if (NULL == req)
229     REFUSE_WITH_404;
230 
231   if ((req->method == "INVITE") && (retarget(req->r_uri, values, call_profile))){
232       return;
233   }
234 
235   REFUSE_WITH_404;
236 }
237 
connect(const string & cc_name,const string & ltag,SBCCallProfile * call_profile,const string & other_tag,int connect_ts_sec,int connect_ts_usec)238 void CCRegistrar::connect(const string& cc_name, const string& ltag,
239 			 SBCCallProfile* call_profile,
240 			 const string& other_tag,
241 			 int connect_ts_sec, int connect_ts_usec) {
242   DBG("call '%s' connecting\n", ltag.c_str());
243 }
244 
end(const string & cc_name,const string & ltag,SBCCallProfile * call_profile,int end_ts_sec,int end_ts_usec)245 void CCRegistrar::end(const string& cc_name, const string& ltag,
246 		     SBCCallProfile* call_profile,
247 		     int end_ts_sec, int end_ts_usec) {
248   DBG("call '%s' ending\n", ltag.c_str());
249 }
250 
route(const string & cc_name,SBCCallProfile * call_profile,const AmSipRequest * ood_req,const AmArg & values,AmArg & res)251 void CCRegistrar::route(const string& cc_name,
252 			SBCCallProfile* call_profile, const AmSipRequest* ood_req,
253 			const AmArg& values, AmArg& res)
254 {
255   DBG("CCRegistrar: route '%s %s'\n", ood_req->method.c_str(), ood_req->r_uri.c_str());
256 
257 
258   if (ood_req->method == "REGISTER"){
259     RegisterCacheCtx reg_cache_ctx;
260 
261     // reply 200 if possible, else continue
262     bool replied = RegisterCache::instance()->saveSingleContact(reg_cache_ctx, *ood_req);
263 
264     if(replied) {
265       DBG("replied!");
266       res.push(AmArg());
267       AmArg& res_cmd = res.back();
268       res_cmd[SBC_CC_ACTION] = SBC_CC_DROP_ACTION;
269     }
270   } else {
271     if (retarget(ood_req->r_uri, values, call_profile)){
272       return;
273     }
274     REFUSE_WITH_404;
275     return;
276   }
277 }
278 
279