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