1 /*
2  *
3  * Inter Asterisk Exchange 2
4  *
5  * Implementation of the IAX2 extensions to the OpalEndpoint class.
6  * There is one instance of this class in the Opal environemnt.
7  *
8  * Open Phone Abstraction Library (OPAL)
9  *
10  * Copyright (c) 2005 Indranet Technologies Ltd.
11  *
12  * The contents of this file are subject to the Mozilla Public License
13  * Version 1.0 (the "License"); you may not use this file except in
14  * compliance with the License. You may obtain a copy of the License at
15  * http://www.mozilla.org/MPL/
16  *
17  * Software distributed under the License is distributed on an "AS IS"
18  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
19  * the License for the specific language governing rights and limitations
20  * under the License.
21  *
22  * The Original Code is Open Phone Abstraction Library.
23  *
24  * The Initial Developer of the Original Code is Indranet Technologies Ltd.
25  *
26  * The author of this code is Derek J Smithies
27  *
28  * $Revision: 23295 $
29  * $Author: rjongbloed $
30  * $Date: 2009-08-28 01:38:49 -0500 (Fri, 28 Aug 2009) $
31  */
32 
33 #include <ptlib.h>
34 #include <opal/buildopts.h>
35 
36 #if OPAL_IAX2
37 
38 #ifdef P_USE_PRAGMA
39 #pragma implementation "processor.h"
40 #endif
41 
42 #include <iax2/causecode.h>
43 #include <iax2/frame.h>
44 #include <iax2/iax2con.h>
45 #include <iax2/iax2ep.h>
46 #include <iax2/iax2medstrm.h>
47 #include <iax2/ies.h>
48 #include <iax2/processor.h>
49 #include <iax2/regprocessor.h>
50 #include <iax2/sound.h>
51 #include <iax2/transmit.h>
52 
53 #include <ptclib/random.h>
54 #include <typeinfo>
55 
56 #define new PNEW
57 
IAX2RegProcessor(IAX2EndPoint & ep,const PString & inHost,const PString & inUsername,const PString & inPassword,PINDEX inRegistrationRefreshTime)58 IAX2RegProcessor::IAX2RegProcessor(
59     IAX2EndPoint &ep,
60     const PString & inHost,
61     const PString & inUsername,
62     const PString & inPassword,
63     PINDEX inRegistrationRefreshTime
64     )
65   : IAX2Processor(ep), host(inHost), userName(inUsername), password(inPassword),
66     registrationRefreshTime(inRegistrationRefreshTime)
67 {
68   registrationTimer.SetNotifier(PCREATE_NOTIFIER(OnDoRegistration));
69   registrationState = registrationStart;
70 
71   PIPSocket::Address ip;
72   if (!PIPSocket::GetHostAddress(host, ip)) {
73      PTRACE(2, "Failed to lookup " << host);
74   }
75 
76   remote.SetRemoteAddress(ip);
77 
78   Activate();
79   Resume();
80 }
81 
~IAX2RegProcessor()82 IAX2RegProcessor::~IAX2RegProcessor()
83 {
84 }
85 
PrintOn(ostream & strm) const86 void IAX2RegProcessor::PrintOn(ostream & strm) const
87 {
88   strm << "In registration call with " << callToken  << endl
89        << "  Call has been up for " << setprecision(0) << setw(8)
90        << (PTimer::Tick() - callStartTick) << " milliseconds" << endl;
91 }
92 
OnNoResponseTimeout()93 void IAX2RegProcessor::OnNoResponseTimeout()
94 {
95   PWaitAndSignal m(stateMutex);
96 
97   if (registrationState == registrationHappening) {
98     //retry registration when the refresh time occurs
99     registrationState = registrationWait;
100     registrationTimer = registrationRefreshTime;
101 
102     endpoint.OnRegistered(host, userName, PTrue);
103   }
104 
105   if (registrationState == registrationUnregistering) {
106     registrationState = registrationUnregistered;
107     endpoint.OnUnregistered(host, userName, PTrue);
108     Terminate();
109   }
110 }
111 
ProcessLists()112 void IAX2RegProcessor::ProcessLists()
113 {
114   PWaitAndSignal m(stateMutex);
115   while (ProcessOneIncomingEthernetFrame()) {
116     ;
117   }
118 
119   if (registrationState == registrationStart) {
120     PTRACE(2, "starting registration cycle");
121     ResetCall();
122 
123     IAX2FullFrameProtocol * f = new IAX2FullFrameProtocol(this, IAX2FullFrameProtocol::cmdRegReq,
124       IAX2FullFrameProtocol::callIrrelevant);
125     f->AppendIe(new IAX2IeUserName(userName));
126     f->AppendIe(new IAX2IeRefresh((short)registrationRefreshTime));
127 
128     TransmitFrameToRemoteEndpoint(f);
129     registrationState = registrationHappening;
130     StartNoResponseTimer();
131   } else if (registrationState == registrationUnregisterStart) {
132     PTRACE(2, "starting registration release");
133     ResetCall();
134 
135     IAX2FullFrameProtocol * f = new IAX2FullFrameProtocol(this, IAX2FullFrameProtocol::cmdRegRel,
136       IAX2FullFrameProtocol::callIrrelevant);
137     f->AppendIe(new IAX2IeUserName(userName));
138 
139     TransmitFrameToRemoteEndpoint(f);
140     registrationState = registrationUnregistering;
141     StartNoResponseTimer();
142   }
143 }
144 
ProcessNetworkFrame(IAX2MiniFrame *)145 void IAX2RegProcessor::ProcessNetworkFrame(IAX2MiniFrame * /*src*/)
146 {
147   PTRACE(1, "unexpected Mini Frame");
148 }
149 
ProcessFullFrame(IAX2FullFrame & fullFrame)150 void IAX2RegProcessor::ProcessFullFrame(IAX2FullFrame & fullFrame)
151 {
152   switch(fullFrame.GetFrameType()) {
153   case IAX2FullFrame::iax2ProtocolType:
154     PTRACE(3, "Build matching full frame    fullFrameProtocol");
155     ProcessNetworkFrame(new IAX2FullFrameProtocol(fullFrame));
156     break;
157 
158   default:
159     PTRACE(3, "Build matching full frame, Type not expected");
160   }
161 
162 }
163 
ProcessNetworkFrame(IAX2FullFrameProtocol * src)164 PBoolean IAX2RegProcessor::ProcessNetworkFrame(IAX2FullFrameProtocol * src)
165 {
166   PTRACE(3, "ProcessNetworkFrame(IAX2FullFrameProtocol * src)");
167   src->CopyDataFromIeListTo(ieData);
168 
169   //check if the common method can process it?
170   if (IAX2Processor::ProcessNetworkFrame(src))
171     return PTrue;
172 
173   if (registrationState == registrationHappening) {
174     switch (src->GetSubClass()) {
175     case IAX2FullFrameProtocol::cmdRegAuth:
176       ProcessIaxCmdRegAuth(src);
177       break;
178     case IAX2FullFrameProtocol::cmdRegAck:
179       ProcessIaxCmdRegAck(src);
180       break;
181     case IAX2FullFrameProtocol::cmdRegRej:
182       ProcessIaxCmdRegRej(src);
183       break;
184     default:
185       PTRACE(1, "Process Full Frame Protocol registering, Type not expected");
186       delete src;
187     }
188   }
189 
190   if (registrationState == registrationUnregistering) {
191     switch (src->GetSubClass()) {
192     case IAX2FullFrameProtocol::cmdRegAuth:
193       ProcessIaxCmdUnRegAuth(src);
194       break;
195     case IAX2FullFrameProtocol::cmdRegAck:
196       ProcessIaxCmdUnRegAck(src);
197       break;
198     case IAX2FullFrameProtocol::cmdRegRej:
199       ProcessIaxCmdUnRegRej(src);
200       break;
201     default:
202       PTRACE(1, "Process Full Frame Protocol unregistering, Type not expected");
203       delete src;
204       return PFalse;
205     }
206   }
207   return PTrue;
208 }
209 
ProcessIaxCmdRegAuth(IAX2FullFrameProtocol * src)210 void IAX2RegProcessor::ProcessIaxCmdRegAuth(IAX2FullFrameProtocol *src)
211 {
212   PTRACE(3, "ProcessIaxCmdRegAuth(IAX2FullFrameProtocol * src)");
213 
214   StopNoResponseTimer();
215 
216   IAX2FullFrameProtocol *f = new IAX2FullFrameProtocol(this, IAX2FullFrameProtocol::cmdRegReq,
217     IAX2FullFrameProtocol::callIrrelevant);
218 
219   f->AppendIe(new IAX2IeUserName(userName));
220   Authenticate(f, password);
221   f->AppendIe(new IAX2IeRefresh((short)registrationRefreshTime));
222 
223   TransmitFrameToRemoteEndpoint(f);
224 
225   StartNoResponseTimer();
226   delete src;
227 }
228 
ProcessIaxCmdRegAck(IAX2FullFrameProtocol * src)229 void IAX2RegProcessor::ProcessIaxCmdRegAck(IAX2FullFrameProtocol * src)
230 {
231   PINDEX refreshTime;
232 
233   PTRACE(3, "ProcessIaxCmdRegAuth(IAX2FullFrameProtocol * src)");
234 
235   StopNoResponseTimer();
236   refreshTime = ieData.refresh;
237   if (refreshTime < 10) //prevent very low refresh times
238     refreshTime = 10;
239 
240   //convert time to milliseconds and give it a random refresh time
241   //in order to not overload the server.  The time is slightly less than
242   //the time requested by the server this is to allow time for the packets
243   //to travel there.
244   int minRefreshTime = (int)refreshTime * 900;
245   int randomPart = (int)(regRandom.Number() % refreshTime) * 50;
246   PTRACE(4, "Requested max refresh time " << refreshTime << " Chosen refresh time "
247     << (minRefreshTime + randomPart));
248   registrationTimer = PTimeInterval(minRefreshTime + randomPart);
249 
250   endpoint.OnRegistered(host, userName, PFalse);
251   registrationState = registrationWait;
252 
253   SendAckFrame(src);
254   delete src;
255 }
256 
ProcessIaxCmdRegRej(IAX2FullFrameProtocol * src)257 void IAX2RegProcessor::ProcessIaxCmdRegRej(IAX2FullFrameProtocol * src)
258 {
259   PTRACE(3, "ProcessIaxCmdRej(IAX2FullFrameProtocol * src)");
260 
261   StopNoResponseTimer();
262   endpoint.OnRegistered(host, userName, PTrue);
263   /* This is an optimistic approach - retrying the registration
264      once the timer event is triggered. even though our password
265      failed.*/
266   registrationState = registrationWait;
267   registrationTimer = registrationRefreshTime * 1000; //convert to milliseconds
268 
269   SendAckFrame(src);
270   delete src;
271 }
272 
ProcessIaxCmdUnRegAuth(IAX2FullFrameProtocol * src)273 void IAX2RegProcessor::ProcessIaxCmdUnRegAuth(IAX2FullFrameProtocol *src)
274 {
275   PTRACE(3, "ProcessIaxCmdUnRegAuth(IAX2FullFrameProtocol * src)");
276 
277   StopNoResponseTimer();
278 
279   IAX2FullFrameProtocol *f = new IAX2FullFrameProtocol(this, IAX2FullFrameProtocol::cmdRegRel,
280     IAX2FullFrameProtocol::callIrrelevant);
281 
282   f->AppendIe(new IAX2IeUserName(userName));
283   Authenticate(f, password);
284   TransmitFrameToRemoteEndpoint(f);
285 
286   StartNoResponseTimer();
287   delete src;
288 }
289 
ProcessIaxCmdUnRegAck(IAX2FullFrameProtocol * src)290 void IAX2RegProcessor::ProcessIaxCmdUnRegAck(IAX2FullFrameProtocol *src)
291 {
292   PTRACE(3, "ProcessIaxCmdUnRegAck(IAX2FullFrameProtocol * src)");
293 
294   StopNoResponseTimer();
295   SendAckFrame(src);
296 
297   registrationState = registrationUnregistered;
298   endpoint.OnUnregistered(host, userName, PFalse);
299   Terminate();
300   delete src;
301 }
302 
ProcessIaxCmdUnRegRej(IAX2FullFrameProtocol * src)303 void IAX2RegProcessor::ProcessIaxCmdUnRegRej(IAX2FullFrameProtocol *src)
304 {
305   PTRACE(3, "ProcessIaxCmdRej(IAX2FullFrameProtocol * src)");
306 
307   //if the server didn't let us unregister then lets
308   //just ditch it.
309   StopNoResponseTimer();
310   registrationState = registrationUnregistered;
311   endpoint.OnUnregistered(host, userName, PTrue);
312   Terminate();
313   delete src;
314 }
315 
OnDoRegistration(PTimer &,INT)316 void IAX2RegProcessor::OnDoRegistration(PTimer &, INT)
317 {
318   //this is run in a different thread
319   PWaitAndSignal m(stateMutex);
320   PTRACE(3, "Registration timer called");
321   if (registrationState == registrationWait) {
322     registrationState = registrationStart;
323     Activate();
324   }
325 }
326 
Unregister()327 void IAX2RegProcessor::Unregister()
328 {
329   PTRACE(2, "Unregistration called");
330   {
331     PWaitAndSignal m(stateMutex);
332     if (registrationState != registrationUnregistered) {
333       registrationState = registrationUnregisterStart;
334       Activate();
335     }
336   }
337 
338   //this will block until the registration is released or there is
339   //a timeout.  This is not optimal because the thread calling this
340   //method will be suspended.  In the future there will be another thread
341   //just closing down various processors to prevent this problem.
342   WaitForTermination();
343 }
344 
ResetCall()345 void IAX2RegProcessor::ResetCall()
346 {
347   //to prevent creating a new regprocessor instance for each
348   //registration time.  We just emulate a new call by resetting
349   //our source call number and the sequence numbers.
350 
351   PINDEX callno = endpoint.NextSrcCallNumber(this);
352   if (callno != P_MAX_INDEX)
353     remote.SetSourceCallNumber(callno);
354 
355   sequence.ZeroAllValues();
356   callStartTick = PTimer::Tick();
357 }
358 
359 
360 #endif // OPAL_IAX2
361