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