1 /*
2  * Copyright (C) 2006 iptego GmbH
3  * Copyright (C) 2011 Stefan Sayer
4  *
5  * This file is part of SEMS, a free SIP media server.
6  *
7  * SEMS is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version. This program is released under
11  * the GPL with the additional exemption that compiling, linking,
12  * and/or using OpenSSL is allowed.
13  *
14  * For a license to use the SEMS software under conditions
15  * other than those described here, or to purchase support for this
16  * software, please contact iptel.org by e-mail at the following addresses:
17  *    info@iptel.org
18  *
19  * SEMS is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  */
28 
29 #include "AmSipRegistration.h"
30 #include "AmSession.h"
31 #include "AmSessionContainer.h"
AmSIPRegistration(const string & handle,const SIPRegistrationInfo & info,const string & sess_link)32 AmSIPRegistration::AmSIPRegistration(const string& handle,
33 				     const SIPRegistrationInfo& info,
34 				     const string& sess_link)
35   : dlg(this),
36     cred(info.domain, info.auth_user, info.pwd),
37     info(info),
38     sess_link(sess_link),
39     seh(NULL),
40     reg_begin(0),
41     reg_expires(0),
42     reg_send_begin(0),
43     expires_interval(3600),
44     active(false),
45     remove(false),
46     waiting_result(false),
47     unregistering(false)
48 {
49   req.user     = info.user;
50   req.method   = "REGISTER";
51   req.r_uri    = "sip:"+info.domain;
52   req.from     = info.name+" <sip:"+info.user+"@"+info.domain+">";
53   req.from_uri = "sip:"+info.user+"@"+info.domain;
54   req.from_tag = handle;
55   req.to       = req.from;
56   req.to_tag   = "";
57   req.callid   = AmSession::getNewId();
58   //
59 
60   // clear dlg.callid? ->reregister?
61   dlg.initFromLocalRequest(req);
62   dlg.cseq = 50;
63 }
64 
~AmSIPRegistration()65 AmSIPRegistration::~AmSIPRegistration() {
66   setSessionEventHandler(NULL);
67 }
68 
setRegistrationInfo(const SIPRegistrationInfo & _info)69 void AmSIPRegistration::setRegistrationInfo(const SIPRegistrationInfo& _info) {
70   DBG("updating registration info for '%s@%s'\n",
71       _info.user.c_str(), _info.domain.c_str());
72   info = _info;
73 
74   cred.realm = info.domain;
75   cred.user = info.user;
76   cred.pwd = info.pwd;
77 
78   req.user     = info.user;
79   req.r_uri    = "sip:"+info.domain;
80   req.from     = info.name+" <sip:"+info.user+"@"+info.domain+">";
81   req.from_uri = "sip:"+info.user+"@"+info.domain;
82   req.to       = req.from;
83   req.to_tag   = "";
84 
85   // to trigger setting dlg identifiers
86   dlg.setCallid(string());
87 
88   dlg.initFromLocalRequest(req);
89 }
90 
setSessionEventHandler(AmSessionEventHandler * new_seh)91 void AmSIPRegistration::setSessionEventHandler(AmSessionEventHandler* new_seh) {
92   if (seh)
93     delete seh;
94   seh = new_seh;
95 }
96 
setExpiresInterval(unsigned int desired_expires)97 void AmSIPRegistration::setExpiresInterval(unsigned int desired_expires) {
98   expires_interval = desired_expires;
99 }
100 
doRegistration()101 bool AmSIPRegistration::doRegistration()
102 {
103   bool res = true;
104 
105   waiting_result = true;
106   unregistering = false;
107 
108   req.to_tag     = "";
109   req.r_uri    = "sip:"+info.domain;
110 
111   dlg.setRemoteTag(string());
112   dlg.setRemoteUri(req.r_uri);
113 
114   // set outbound proxy as next hop
115   if (!info.proxy.empty()) {
116     dlg.outbound_proxy = info.proxy;
117   } else if (!AmConfig::OutboundProxy.empty()) {
118     dlg.outbound_proxy = AmConfig::OutboundProxy;
119   }
120 
121   string hdrs = SIP_HDR_COLSP(SIP_HDR_EXPIRES) +
122     int2str(expires_interval) + CRLF;
123 
124   int flags=0;
125   if(!info.contact.empty()) {
126     hdrs += SIP_HDR_COLSP(SIP_HDR_CONTACT) "<"
127       + info.contact + ">" + CRLF;
128     flags = SIP_FLAGS_NOCONTACT;
129   }
130 
131   if (dlg.sendRequest(req.method, NULL, hdrs, flags) < 0) {
132     ERROR("failed to send registration.\n");
133     res = false;
134     waiting_result = false;
135   }
136 
137   // save TS
138   reg_send_begin  = time(NULL);
139   return res;
140 }
141 
doUnregister()142 bool AmSIPRegistration::doUnregister()
143 {
144   bool res = true;
145 
146   waiting_result = true;
147   unregistering = true;
148 
149   req.to_tag     = "";
150   req.r_uri      = "sip:"+info.domain;
151   dlg.setRemoteTag(string());
152   dlg.setRemoteUri(req.r_uri);
153 
154   // set outbound proxy as next hop
155   if (!info.proxy.empty()) {
156     dlg.outbound_proxy = info.proxy;
157   } else if (!AmConfig::OutboundProxy.empty())
158     dlg.outbound_proxy = AmConfig::OutboundProxy;
159 
160   int flags=0;
161   string hdrs = SIP_HDR_COLSP(SIP_HDR_EXPIRES) "0" CRLF;
162   if(!info.contact.empty()) {
163     hdrs = SIP_HDR_COLSP(SIP_HDR_CONTACT) "<";
164     hdrs += info.contact + ">" + CRLF;
165     flags = SIP_FLAGS_NOCONTACT;
166   }
167 
168   if (dlg.sendRequest(req.method, NULL, hdrs, flags) < 0) {
169     ERROR("failed to send deregistration.\n");
170     res = false;
171     waiting_result = false;
172   }
173 
174   // save TS
175   reg_send_begin  = time(NULL);
176   return res;
177 }
178 
onSendRequest(AmSipRequest & req,int & flags)179 void AmSIPRegistration::onSendRequest(AmSipRequest& req, int& flags)
180 {
181   if (seh)
182     seh->onSendRequest(req,flags);
183 }
184 
onSendReply(const AmSipRequest & req,AmSipReply & reply,int & flags)185 void AmSIPRegistration::onSendReply(const AmSipRequest& req, AmSipReply& reply,
186 				    int& flags) {
187   if (seh)
188     seh->onSendReply(req,reply,flags);
189 }
190 
getState()191 AmSIPRegistration::RegistrationState AmSIPRegistration::getState() {
192   if (active)
193     return RegisterActive;
194   if (waiting_result)
195     return RegisterPending;
196 
197   return RegisterExpired;
198 }
199 
getUnregistering()200 bool AmSIPRegistration::getUnregistering() {
201   return unregistering;
202 }
203 
getExpiresLeft()204 unsigned int AmSIPRegistration::getExpiresLeft() {
205   long diff = reg_begin + reg_expires  - time(NULL);
206   if (diff < 0)
207     return 0;
208   else
209     return diff;
210 }
211 
getExpiresTS()212 time_t AmSIPRegistration::getExpiresTS() {
213   return reg_begin + reg_expires;
214 }
215 
onRegisterExpired()216 void AmSIPRegistration::onRegisterExpired() {
217   if (sess_link.length()) {
218     AmSessionContainer::instance()->postEvent(sess_link,
219 					      new SIPRegistrationEvent(SIPRegistrationEvent::RegisterTimeout,
220 								       req.from_tag));
221   }
222   DBG("Registration '%s' expired.\n", (info.user+"@"+info.domain).c_str());
223   active = false;
224   remove = true;
225 }
226 
onRegisterSendTimeout()227 void AmSIPRegistration::onRegisterSendTimeout() {
228   if (sess_link.length()) {
229     AmSessionContainer::instance()->
230       postEvent(sess_link,
231 		new SIPRegistrationEvent(SIPRegistrationEvent::RegisterSendTimeout,
232 					 req.from_tag));
233   }
234   DBG("Registration '%s' REGISTER request timeout.\n",
235       (info.user+"@"+info.domain).c_str());
236   active = false;
237   remove = true;
238 }
239 
registerSendTimeout(time_t now_sec)240 bool AmSIPRegistration::registerSendTimeout(time_t now_sec) {
241   return now_sec > reg_send_begin + REGISTER_SEND_TIMEOUT;
242 }
243 
timeToReregister(time_t now_sec)244 bool AmSIPRegistration::timeToReregister(time_t now_sec) {
245   //   	if (active)
246   //   		DBG("compare %lu with %lu\n",(reg_begin+reg_expires), (unsigned long)now_sec);
247   return (((unsigned long)reg_begin+ reg_expires/2) < (unsigned long)now_sec);
248 }
249 
registerExpired(time_t now_sec)250 bool AmSIPRegistration::registerExpired(time_t now_sec) {
251   return ((reg_begin+reg_expires) < (unsigned int)now_sec);
252 }
253 
onSipReply(const AmSipRequest & req,const AmSipReply & reply,AmBasicSipDialog::Status old_dlg_status)254 void AmSIPRegistration::onSipReply(const AmSipRequest& req,
255 				   const AmSipReply& reply,
256 				   AmBasicSipDialog::Status old_dlg_status)
257 {
258   if ((seh!=NULL) && seh->onSipReply(req,reply, old_dlg_status))
259     return;
260 
261   if (reply.code>=200)
262     waiting_result = false;
263 
264   if ((reply.code>=200)&&(reply.code<300)) {
265 
266     string contacts = reply.contact;
267     if (contacts.empty())
268       contacts = getHeader(reply.hdrs, "Contact", "m", true);
269 
270     if (unregistering) {
271       DBG("received positive reply to De-REGISTER\n");
272 
273       active = false;
274       remove = true;
275       if (!contacts.length()) {
276 	DBG("no contacts registered any more\n");
277       }
278       if (sess_link.length()) {
279 	AmSessionContainer::instance()->
280 	  postEvent(sess_link,
281 		    new SIPRegistrationEvent(SIPRegistrationEvent::RegisterNoContact,
282 					     req.from_tag,
283 					     reply.code, reply.reason));
284       }
285 
286     } else {
287       DBG("positive reply to REGISTER!\n");
288 
289       size_t end  = 0;
290       string local_contact_hdr = info.contact.empty() ?
291 	dlg.getContactUri() : info.contact;
292       local_contact.parse_contact(local_contact_hdr, (size_t)0, end);
293       local_contact.dump();
294 
295       bool found = false;
296 
297       if (!contacts.length()) {
298 	// should not happen - positive reply without contact
299 	DBG("no contacts registered any more\n");
300 	active = false;
301 	remove = true;
302       } else {
303 	end = 0;
304 	while (!found) {
305 	  if (contacts.length() == end)
306 	    break;
307 
308 	  if (!server_contact.parse_contact(contacts, end, end)) {
309 	    ERROR("while parsing contact\n");
310 	    break;
311 	  }
312 	  server_contact.dump();
313 
314 	  if (server_contact.isEqual(local_contact)) {
315 	    DBG("contact found\n");
316 	    found = active = true;
317 
318 	    if (str2i(server_contact.params["expires"], reg_expires)) {
319 	      ERROR("could not extract expires value, default to 300.\n");
320 	      reg_expires = 300;
321 	    }
322 	    DBG("got an expires of %d\n", reg_expires);
323 	    // save TS
324 	    reg_begin = time(0);
325 
326 	    if (sess_link.length()) {
327 	      DBG("posting SIPRegistrationEvent to '%s'\n", sess_link.c_str());
328 	      AmSessionContainer::instance()->
329 		postEvent(sess_link,
330 			  new SIPRegistrationEvent(SIPRegistrationEvent::RegisterSuccess,
331 						   req.from_tag,
332 						   reply.code, reply.reason));
333 	    }
334 	    break;
335 	  }
336 	}
337       }
338       if (!found) {
339 	if (sess_link.length()) {
340 	  AmSessionContainer::instance()->
341 	    postEvent(sess_link,
342 		      new SIPRegistrationEvent(SIPRegistrationEvent::RegisterNoContact,
343 					       req.from_tag,
344 					       reply.code, reply.reason));
345 	}
346 	DBG("no matching Contact - deregistered.\n");
347 	active = false;
348 	remove = true;
349       }
350     }
351 
352   } else if (reply.code >= 300) {
353     DBG("Registration failed.\n");
354     if (sess_link.length()) {
355       AmSessionContainer::instance()->
356 	postEvent(sess_link,
357 		  new SIPRegistrationEvent(SIPRegistrationEvent::RegisterFailed,
358 					   req.from_tag,
359 					   reply.code, reply.reason));
360     }
361     active = false;
362     remove = true;
363   }
364 }
365 
366