1 /*
2 * gnugknat.cxx
3 *
4 * GnuGk NAT Traversal class.
5 *
6 * h323plus library
7 *
8 * Copyright (c) 2008 ISVO (Asia) Pte. Ltd.
9 *
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.1 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
14 *
15 * Alternatively, the contents of this file may be used under the terms
16 * of the General Public License (the "GNU License"), in which case the
17 * provisions of GNU License are applicable instead of those
18 * above. If you wish to allow use of your version of this file only
19 * under the terms of the GNU License and not to allow others to use
20 * your version of this file under the MPL, indicate your decision by
21 * deleting the provisions above and replace them with the notice and
22 * other provisions required by the GNU License. If you do not delete
23 * the provisions above, a recipient may use your version of this file
24 * under either the MPL or the GNU License."
25 *
26 * Software distributed under the License is distributed on an "AS IS"
27 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
28 * the License for the specific language governing rights and limitations
29 * under the License.
30 *
31 *
32 * The Initial Developer of the Original Code is ISVO (Asia) Pte. Ltd.
33 *
34 * Contributor(s): ______________________________________.
35 *
36 * $Id$
37 *
38 *
39 */
40
41 #include <ptlib.h>
42 #include <h323pdu.h>
43
44 #ifdef H323_GNUGK
45
46 #include "gnugknat.h"
47
48 #if defined(_WIN32) && !defined(P_FORCE_STATIC_PLUGIN)
49 #error "gnugknat.cxx must be compiled without precompiled headers"
50 #endif
51
52 #if PTLIB_VER >= 2130
53 PCREATE_NAT_PLUGIN(GnuGk, "GnuGk");
54 #else
55 PCREATE_NAT_PLUGIN(GnuGk);
56 #endif
57
58
59 WORD GNUGK_Feature::keepalive = 10;
60
61 GNUGKTransport * GNUGK_Feature::curtransport = NULL;
62 PBoolean GNUGK_Feature::connectionlost = FALSE;
63
64 ///////////////////////////////////////////////////////////////////////////////////
65
66 // Listening/Keep Alive Thread
67
68 class GNUGKTransportThread : public PThread
69 {
70 PCLASSINFO(GNUGKTransportThread, PThread)
71
72 public:
73 GNUGKTransportThread(H323EndPoint & endpoint, GNUGKTransport * transport, WORD KeepAlive =0);
74
75 protected:
76 void Main();
77 PDECLARE_NOTIFIER(PTimer, GNUGKTransportThread, Ping); /// Timer to notify to poll for External IP
78 PTimer Keep; /// Polling Timer
79 PBoolean isConnected;
80 GNUGKTransport * transport;
81 WORD keepAlive;
82
83 PTime lastupdate;
84
85 };
86
87 /////////////////////////////////////////////////////////////////////////////
88
GNUGKTransportThread(H323EndPoint & ep,GNUGKTransport * t,WORD KeepAlive)89 GNUGKTransportThread::GNUGKTransportThread(H323EndPoint & ep, GNUGKTransport * t, WORD KeepAlive)
90 : PThread(ep.GetSignallingThreadStackSize(),
91 AutoDeleteThread,
92 NormalPriority,
93 "H225 Answer:%0x"),
94 transport(t)
95 {
96
97 isConnected = FALSE;
98 keepAlive = KeepAlive;
99
100 if (keepAlive > 0 ) {
101 // Send the first PDU.
102 transport->InitialPDU();
103
104 Keep.SetNotifier(PCREATE_NOTIFIER(Ping));
105 Keep.RunContinuous(keepAlive * 1000);
106 }
107
108 // Start the Thread
109 Resume();
110 }
111
Ping(PTimer &,H323_INT)112 void GNUGKTransportThread::Ping(PTimer &, H323_INT)
113 {
114
115 // Fix for some PC's that fail on the runcontinuously time interval.
116 PSyncPoint handlewait;
117
118 PTime curTime = PTime();
119 if ((curTime - lastupdate) < PTimeInterval(GNUGK_Feature::keepalive * 1000))
120 handlewait.Wait(lastupdate + PTimeInterval(GNUGK_Feature::keepalive * 1000) - curTime);
121
122
123 if (transport->isCall() || /// We have call or we are closing down
124 transport->CloseTransport())
125 Keep.Stop();
126 else /// Stop what we are doing
127 transport->InitialPDU();
128
129 lastupdate = PTime();
130 }
131
Main()132 void GNUGKTransportThread::Main()
133 {
134 PTRACE(3, "GNUGK\tStarted Listening-KeepAlive Thread");
135
136 PBoolean ret = TRUE;
137 while ((transport->IsOpen()) && // Transport is Open
138 (!isConnected) && // Does not have a call connection
139 (ret) && // is not a Failed connection
140 (!transport->CloseTransport())) { // not close due to shutdown
141 ret = transport->HandleGNUGKSignallingChannelPDU(this);
142
143 if (!ret && transport->CloseTransport()) { // Closing down Instruction
144 PTRACE(3, "GNUGK\tShutting down GnuGk Thread");
145 GNUGK_Feature::curtransport = NULL;
146 transport->ConnectionLost(TRUE);
147
148 } else if (!ret) { // We have a socket failure wait 1 sec and try again.
149 PTRACE(3, "GNUGK\tConnection Lost! Retrying Connection..");
150 transport->ConnectionLost(TRUE);
151 while (!transport->CloseTransport() &&
152 !transport->Connect()) {
153 PTRACE(3, "GNUGK\tReconnect Failed! Waiting 1 sec");
154 PThread::Sleep(1000);
155 }
156
157 if (!transport->CloseTransport()) {
158 PTRACE(3, "GNUGK\tConnection ReEstablished");
159 transport->ConnectionLost(FALSE);
160 ret = TRUE; // Signal that the connection has been ReEstablished.
161 }
162 } else { // We are connected to a call on this thread
163 isConnected = TRUE;
164 }
165 }
166
167 PTRACE(3, "GNUGK\tTransport Closed");
168 }
169
170 ///////////////////////////////////////////////////////////////////////////////////////
171
GNUGKTransport(H323EndPoint & endpoint,PIPSocket::Address binding,GNUGK_Feature * feat,PString & gkid)172 GNUGKTransport::GNUGKTransport(H323EndPoint & endpoint,
173 PIPSocket::Address binding,
174 GNUGK_Feature * feat,
175 PString & gkid
176 )
177 : H323TransportTCP(endpoint, binding), GKid(gkid), Feature(feat)
178 {
179 GNUGK_Feature::curtransport = this;
180 ReadTimeOut = PMaxTimeInterval;
181 isConnected = FALSE;
182 closeTransport = FALSE;
183 remoteShutDown = FALSE;
184 }
185
~GNUGKTransport()186 GNUGKTransport::~GNUGKTransport()
187 {
188 Close();
189 }
190
HandleGNUGKSignallingSocket(H323SignalPDU & pdu)191 PBoolean GNUGKTransport::HandleGNUGKSignallingSocket(H323SignalPDU & pdu)
192 {
193 for (;;) {
194
195 if (!IsOpen())
196 return FALSE;
197
198 H323SignalPDU rpdu;
199 if (!rpdu.Read(*this)) {
200 PTRACE(3, "GNUGK\tSocket Read Failure");
201 if (GetErrorNumber(PChannel::LastReadError) == 0) {
202 PTRACE(3, "GNUGK\tRemote SHUT DOWN or Intermediary Shutdown!");
203 remoteShutDown = TRUE;
204 }
205 return FALSE;
206 } else if ((rpdu.GetQ931().GetMessageType() == Q931::InformationMsg) &&
207 (endpoint.HandleUnsolicitedInformation(rpdu))) {
208 // Handle unsolicited Information Message
209 } else if (rpdu.GetQ931().GetMessageType() == Q931::SetupMsg) {
210 pdu = rpdu;
211 return TRUE;
212 } else {
213 PTRACE(3, "GNUGK\tUnknown PDU Received");
214 return FALSE;
215 }
216
217 }
218 }
219
HandleGNUGKSignallingChannelPDU(PThread * thread)220 PBoolean GNUGKTransport::HandleGNUGKSignallingChannelPDU(PThread * thread)
221 {
222
223 H323SignalPDU pdu;
224 if (!HandleGNUGKSignallingSocket(pdu)) {
225 if (remoteShutDown) { // Intentional Shutdown?
226 GNUGK_Feature::curtransport = NULL;
227 Close();
228 }
229 return FALSE;
230 }
231
232 // Create a new transport to the GK as this one will be closed at the end of the call.
233 isConnected = TRUE;
234 GNUGK_Feature::curtransport = NULL;
235 CreateNewTransport();
236
237 // Process the Tokens
238 unsigned callReference = pdu.GetQ931().GetCallReference();
239 PString token = endpoint.BuildConnectionToken(*this, callReference, TRUE);
240
241 H323Connection * connection = endpoint.CreateConnection(callReference, NULL, this, &pdu);
242 if (connection == NULL) {
243 PTRACE(1, "GNUGK\tEndpoint could not create connection, " <<
244 "sending release complete PDU: callRef=" << callReference);
245 Q931 pdu;
246 pdu.BuildReleaseComplete(callReference, TRUE);
247 PBYTEArray rawData;
248 pdu.Encode(rawData);
249 WritePDU(rawData);
250 return TRUE;
251 }
252
253 PTRACE(3, "GNUGK\tCreated new connection: " << token);
254 connectionsMutex.Wait();
255 endpoint.GetConnections().SetAt(token, connection);
256 connectionsMutex.Signal();
257
258 connection->AttachSignalChannel(token, this, TRUE);
259
260 AttachThread(thread);
261 thread->SetNoAutoDelete();
262
263 if (connection->HandleSignalPDU(pdu)) {
264 // All subsequent PDU's should wait forever
265 SetReadTimeout(PMaxTimeInterval);
266 connection->HandleSignallingChannel();
267 }
268 else {
269 connection->ClearCall(H323Connection::EndedByTransportFail);
270 PTRACE(1, "GNUGK\tSignal channel stopped on first PDU.");
271 }
272
273 return TRUE;
274 }
275
276
WritePDU(const PBYTEArray & pdu)277 PBoolean GNUGKTransport::WritePDU( const PBYTEArray & pdu )
278 {
279 PWaitAndSignal m(WriteMutex);
280 return H323TransportTCP::WritePDU(pdu);
281
282 }
283
ReadPDU(PBYTEArray & pdu)284 PBoolean GNUGKTransport::ReadPDU(PBYTEArray & pdu)
285 {
286 return H323TransportTCP::ReadPDU(pdu);
287 }
288
Connect()289 PBoolean GNUGKTransport::Connect()
290 {
291 PTRACE(4, "GNUGK\tConnecting to GK" );
292 if (!H323TransportTCP::Connect())
293 return FALSE;
294
295 return InitialPDU();
296 }
297
ConnectionLost(PBoolean established)298 void GNUGKTransport::ConnectionLost(PBoolean established)
299 {
300 PWaitAndSignal m(shutdownMutex);
301
302 if (closeTransport)
303 return;
304 PTRACE(4,"GnuGK\tConnection lost " << established
305 << " have " << GNUGK_Feature::connectionlost);
306 if (GNUGK_Feature::connectionlost != established) {
307 GetEndPoint().NATLostConnection(established);
308 GNUGK_Feature::connectionlost = established;
309 }
310 }
311
IsConnectionLost()312 PBoolean GNUGKTransport::IsConnectionLost()
313 {
314 return GNUGK_Feature::connectionlost;
315 }
316
317
InitialPDU()318 PBoolean GNUGKTransport::InitialPDU()
319 {
320 PWaitAndSignal mutex(IntMutex);
321
322 if (!IsOpen())
323 return FALSE;
324
325 PBYTEArray bytes(GKid,GKid.GetLength(), false);
326
327 Q931 qPDU;
328 qPDU.BuildInformation(0,false);
329 qPDU.SetCallState(Q931::CallState_IncomingCallProceeding);
330 qPDU.SetIE(Q931::FacilityIE, bytes);
331
332
333 PBYTEArray rawData;
334 if (!qPDU.Encode(rawData)) {
335 PTRACE(4, "GNUGK\tError Encoding PDU.");
336 return FALSE;
337 }
338
339 if (!WritePDU(rawData)) {
340 PTRACE(4, "GNUGK\tError Writing PDU.");
341 return FALSE;
342 }
343
344 PTRACE(6, "GNUGK\tSent KeepAlive PDU.");
345
346 return TRUE;
347 }
348
SetGKID(const PString & newid)349 PBoolean GNUGKTransport::SetGKID(const PString & newid)
350 {
351 if (GKid != newid) {
352 GKid = newid;
353 return TRUE;
354 }
355 return FALSE;
356 }
357
CreateNewTransport()358 PBoolean GNUGKTransport::CreateNewTransport()
359 {
360 H323TransportAddress remote = GetRemoteAddress();
361 GNUGKTransport * transport = new GNUGKTransport(GetEndPoint(),
362 PIPSocket::Address::GetAny(remote.GetIpVersion()),Feature,GKid);
363
364 transport->InitialiseSecurity(&m_callSecurity);
365 transport->SetRemoteAddress(remote);
366
367 if (transport->Connect()) {
368 PTRACE(3, "GNUGK\tConnected to " << transport->GetRemoteAddress());
369 new GNUGKTransportThread(transport->GetEndPoint(), transport,GNUGK_Feature::keepalive);
370 if (transport->IsConnectionLost())
371 transport->ConnectionLost(FALSE);
372 return TRUE;
373 }
374 return FALSE;
375 }
376
Close()377 PBoolean GNUGKTransport::Close()
378 {
379 PWaitAndSignal m(shutdownMutex);
380
381 PTRACE(4, "GNUGK\tClosing GnuGK NAT channel.");
382 closeTransport = TRUE;
383 return H323TransportTCP::Close();
384 }
385
IsOpen() const386 PBoolean GNUGKTransport::IsOpen () const
387 {
388 return H323Transport::IsOpen();
389 }
390
IsListening() const391 PBoolean GNUGKTransport::IsListening() const
392 {
393 if (isConnected)
394 return FALSE;
395
396 if (h245listener == NULL)
397 return FALSE;
398
399 if (GNUGK_Feature::connectionlost)
400 return FALSE;
401
402 return h245listener->IsOpen();
403 }
404
405 /////////////////////////////////////////////////////////////////////////////
406
GNUGK_Feature(H323EndPoint & EP,H323TransportAddress & remoteAddress,PString gkid,WORD KeepAlive)407 GNUGK_Feature::GNUGK_Feature(H323EndPoint & EP,
408 H323TransportAddress & remoteAddress,
409 PString gkid,
410 WORD KeepAlive )
411 : ep(EP), address(remoteAddress), GKid(gkid)
412 {
413 PTRACE(4, "GNUGK\tCreating GNUGK Feature.");
414 keepalive = KeepAlive;
415 open = CreateNewTransport();
416 }
417
~GNUGK_Feature()418 GNUGK_Feature::~GNUGK_Feature()
419 {
420 if (curtransport != NULL)
421 curtransport->Close();
422 }
423
CreateNewTransport()424 PBoolean GNUGK_Feature::CreateNewTransport()
425 {
426 PTRACE(5, "GNUGK\tCreating Transport.");
427
428 GNUGKTransport * transport = new GNUGKTransport(ep,
429 PIPSocket::Address::GetAny(address.GetIpVersion()), this,GKid);
430 transport->SetRemoteAddress(address);
431
432 if (transport->Connect()) {
433 PTRACE(3, "GNUGK\tConnected to " << transport->GetRemoteAddress());
434 new GNUGKTransportThread(transport->GetEndPoint(), transport,keepalive);
435 return TRUE;
436 }
437
438 PTRACE(3, "GNUGK\tTransport Failure " << transport->GetRemoteAddress());
439 return FALSE;
440 }
441
ReRegister(const PString & newid)442 PBoolean GNUGK_Feature::ReRegister(const PString & newid)
443 {
444 // If there is a change in the gatekeeper id then notify the update socket
445 if ((GNUGK_Feature::curtransport != NULL) && curtransport->SetGKID(newid))
446 return curtransport->InitialPDU(); // Send on existing Transport
447
448 return FALSE;
449
450 }
451
452 ///////////////////////////////////////////////////////////////////////////////////////////
453
PNatMethod_GnuGk()454 PNatMethod_GnuGk::PNatMethod_GnuGk()
455 {
456 EP = NULL;
457 available = false;
458 active = true;
459 }
460
~PNatMethod_GnuGk()461 PNatMethod_GnuGk::~PNatMethod_GnuGk()
462 {
463
464 }
465
466
AttachEndPoint(H323EndPoint * ep)467 void PNatMethod_GnuGk::AttachEndPoint(H323EndPoint * ep)
468 {
469
470 EP = ep;
471
472 WORD portPairBase = ep->GetRtpIpPortBase();
473 WORD portPairMax = ep->GetRtpIpPortMax();
474
475 // Initialise
476 // ExternalAddress = 0;
477 pairedPortInfo.basePort = 0;
478 pairedPortInfo.maxPort = 0;
479 pairedPortInfo.currentPort = 0;
480
481 // Set the Port Pair Information
482 pairedPortInfo.mutex.Wait();
483
484 pairedPortInfo.basePort = (WORD)((portPairBase+1)&0xfffe);
485 if (portPairBase == 0) {
486 pairedPortInfo.basePort = 0;
487 pairedPortInfo.maxPort = 0;
488 }
489 else if (portPairMax == 0)
490 pairedPortInfo.maxPort = (WORD)(pairedPortInfo.basePort+99);
491 else if (portPairMax < portPairBase)
492 pairedPortInfo.maxPort = portPairBase;
493 else
494 pairedPortInfo.maxPort = portPairMax;
495
496 pairedPortInfo.currentPort = pairedPortInfo.basePort;
497
498 pairedPortInfo.mutex.Signal();
499
500 available = FALSE;
501 }
502
GetExternalAddress(PIPSocket::Address &,const PTimeInterval &)503 PBoolean PNatMethod_GnuGk::GetExternalAddress(
504 PIPSocket::Address & /*externalAddress*/, /// External address of router
505 const PTimeInterval & /* maxAge */ /// Maximum age for caching
506 )
507 {
508 return FALSE;
509 }
510
511
CreateSocketPair(PUDPSocket * & socket1,PUDPSocket * & socket2,const PIPSocket::Address & binding)512 PBoolean PNatMethod_GnuGk::CreateSocketPair(
513 PUDPSocket * & socket1,
514 PUDPSocket * & socket2,
515 const PIPSocket::Address & binding
516 )
517 {
518
519 if (pairedPortInfo.basePort == 0 || pairedPortInfo.basePort > pairedPortInfo.maxPort)
520 {
521 PTRACE(1, "GNUGK\tInvalid local UDP port range "
522 << pairedPortInfo.currentPort << '-' << pairedPortInfo.maxPort);
523 return FALSE;
524 }
525
526 socket1 = new GNUGKUDPSocket(); /// Data
527 socket2 = new GNUGKUDPSocket(); /// Signal
528
529 /// Make sure we have sequential ports
530 while ((!OpenSocket(*socket1, pairedPortInfo,binding)) ||
531 (!OpenSocket(*socket2, pairedPortInfo,binding)) ||
532 (socket2->GetPort() != socket1->GetPort() + 1) )
533 {
534 delete socket1;
535 delete socket2;
536 socket1 = new GNUGKUDPSocket(); /// Data
537 socket2 = new GNUGKUDPSocket(); /// Signal
538 }
539
540 PTRACE(5, "GNUGK\tUDP ports "
541 << socket1->GetPort() << '-' << socket2->GetPort());
542
543
544
545 return TRUE;
546 }
547
OpenSocket(PUDPSocket & socket,PortInfo & portInfo,const PIPSocket::Address & binding) const548 PBoolean PNatMethod_GnuGk::OpenSocket(PUDPSocket & socket, PortInfo & portInfo, const PIPSocket::Address & binding) const
549 {
550 PWaitAndSignal mutex(portInfo.mutex);
551
552 WORD startPort = portInfo.currentPort;
553
554 do {
555 portInfo.currentPort++;
556 if (portInfo.currentPort > portInfo.maxPort)
557 portInfo.currentPort = portInfo.basePort;
558
559 if (socket.Listen(binding,1, portInfo.currentPort)) {
560 socket.SetReadTimeout(500);
561 return TRUE;
562 }
563
564 } while (portInfo.currentPort != startPort);
565
566 PTRACE(2, "GNUGK\tFailed to bind to local UDP port in range "
567 << portInfo.currentPort << '-' << portInfo.maxPort);
568 return FALSE;
569 }
570
SetAvailable()571 void PNatMethod_GnuGk::SetAvailable()
572 {
573 EP->NATMethodCallBack(GetName(),1,"Available");
574 available = true;
575 }
576
577 /////////////////////////////////////////////////////////////////////////////////////////////
578
GNUGKUDPSocket()579 GNUGKUDPSocket::GNUGKUDPSocket()
580 {
581 }
582
583
~GNUGKUDPSocket()584 GNUGKUDPSocket::~GNUGKUDPSocket()
585 {
586 }
587
SetSendAddress(const Address & address,WORD port)588 void GNUGKUDPSocket::SetSendAddress(const Address & address,WORD port)
589 {
590 PUDPSocket::SetSendAddress(address,port);
591 #ifdef P_QOS
592 ApplyQoS();
593 #endif
594 }
595
596 #endif // H323_GNUGK
597
598
599
600
601