1 /* H460_std23.cxx
2  *
3  * Copyright (c) 2009 ISVO (Asia) Pte Ltd. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License
6  * Version 1.1 (the "License"); you may not use this file except in
7  * compliance with the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Alternatively, the contents of this file may be used under the terms
11  * of the General Public License (the  "GNU License"), in which case the
12  * provisions of GNU License are applicable instead of those
13  * above. If you wish to allow use of your version of this file only
14  * under the terms of the GNU License and not to allow others to use
15  * your version of this file under the MPL, indicate your decision by
16  * deleting the provisions above and replace them with the notice and
17  * other provisions required by the GNU License. If you do not delete
18  * the provisions above, a recipient may use your version of this file
19  * under either the MPL or the GNU License."
20  *
21  * Software distributed under the License is distributed on an "AS IS"
22  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
23  * the License for the specific language governing rights and limitations
24  * under the License.
25  *
26  * The Original Code is derived from and used in conjunction with the
27  * H323Plus Project (www.h323plus.org/)
28  *
29  * The Initial Developer of the Original Code is ISVO (Asia) Pte Ltd.
30  *
31  *
32  * Contributor(s): ______________________________________.
33  *
34  * $Id$
35  *
36  */
37 #include <ptlib.h>
38 #include <openh323buildopts.h>
39 
40 #ifdef H323_H46023
41 
42 #include <h323.h>
43 #include "h460/h460_std23.h"
44 #include <ptclib/random.h>
45 #include <ptclib/pdns.h>
46 #ifdef H323_H46018
47   #include <h460/h460_std18.h>
48 #endif
49 #ifdef H323_H46019M
50   #include <h460/h46018_h225.h>
51 #endif
52 #ifdef H323_UPnP
53  #include "h460/upnpcp.h"
54 #endif
55 
56 #if _WIN32
57 #pragma message("H.460.23/.24 Enabled. Contact support@h323plus.org for licensing terms.")
58 #else
59 #warning("H.460.23/.24 Enabled. Contact support@h323plus.org for licensing terms.")
60 #endif
61 
62 #ifdef _MSC_VER
63 #pragma warning(disable : 4239)
64 #endif
65 
66 
67 // H.460.23 NAT Detection Feature
68 #define remoteNATOID        1       // bool if endpoint has remote NAT support
69 #define AnnexAOID           2       // bool if endpoint supports H.460.24 Annex A
70 #define localNATOID         3       // bool if endpoint is NATed
71 #define NATDetRASOID        4       // Detected RAS H225_TransportAddress
72 #define STUNServOID         5       // H225_TransportAddress of STUN Server
73 #define NATTypeOID          6       // integer 8 Endpoint NAT Type
74 #define AnnexBOID           7       // bool if endpoint supports H.460.24 Annex B
75 
76 
77 // H.460.24 P2Pnat Feature
78 #define NATProxyOID          1       // PBoolean if gatekeeper will proxy
79 #define remoteMastOID        2       // PBoolean if remote endpoint can assist local endpoint directly
80 #define mustProxyOID         3       // PBoolean if gatekeeper must proxy to reach local endpoint
81 #define calledIsNatOID       4       // PBoolean if local endpoint is behind a NAT/FW
82 #define NatRemoteTypeOID     5       // integer 8 reported NAT type
83 #define apparentSourceOID    6       // H225_TransportAddress of apparent source address of endpoint
84 #define SupAnnexAOID         7       // PBoolean if local endpoint supports H.460.24 Annex A
85 #define NATInstOID           8       // integer 8 Instruction on how NAT is to be Traversed
86 #define SupAnnexBOID         9       // bool if endpoint supports H.460.24 Annex B
87 
88 
89 //////////////////////////////////////////////////////////////////////
90 
91 #if PTLIB_VER >= 2130
92 PCREATE_NAT_PLUGIN(H46024, "H.460.24");
93 #else
94 PCREATE_NAT_PLUGIN(H46024);
95 #endif
96 
PNatMethod_H46024()97 PNatMethod_H46024::PNatMethod_H46024()
98 : mainThread(NULL)
99 {
100     natType = PSTUNClient::UnknownNat;
101     isAvailable = false;
102     isActive = false;
103     feat = NULL;
104 }
105 
~PNatMethod_H46024()106 PNatMethod_H46024::~PNatMethod_H46024()
107 {
108     natType = PSTUNClient::UnknownNat;
109     delete mainThread;
110 }
111 
SetPortInformation(PortInfo & pairedPortInfo,WORD portPairBase,WORD portPairMax)112 void PNatMethod_H46024::SetPortInformation(PortInfo & pairedPortInfo, WORD portPairBase, WORD portPairMax)
113 {
114   pairedPortInfo.basePort = (WORD)((portPairBase+1)&0xfffe);
115   if (portPairBase == 0) {
116     pairedPortInfo.basePort = 0;
117     pairedPortInfo.maxPort = 0;
118   }
119   else if (portPairMax == 0)
120     pairedPortInfo.maxPort = (WORD)(pairedPortInfo.basePort+99);
121   else if (portPairMax < portPairBase)
122     pairedPortInfo.maxPort = portPairBase;
123   else
124     pairedPortInfo.maxPort = portPairMax;
125 
126   pairedPortInfo.currentPort = pairedPortInfo.basePort;
127 
128 }
129 
Start(const PString & server,H460_FeatureStd23 * _feat)130 void PNatMethod_H46024::Start(const PString & server,H460_FeatureStd23 * _feat)
131 {
132     feat = _feat;
133 
134    H323EndPoint * ep = feat->GetEndPoint();
135 
136    SetServer(server);
137 #ifdef H323_H46019M
138     WORD muxBase = ep->GetMultiplexPort();
139     SetPortInformation(multiplexPorts,muxBase-2, muxBase+2);
140     SetPortInformation(standardPorts, ep->GetRtpIpPortBase(), ep->GetRtpIpPortMax());
141     SetPortRanges(muxBase-2, muxBase+2, muxBase-2, muxBase+20);
142 #else
143     SetPortRanges(ep->GetRtpIpPortBase(), ep->GetRtpIpPortMax(), ep->GetRtpIpPortBase(), ep->GetRtpIpPortMax());
144 #endif
145 
146     mainThread  = PThread::Create(PCREATE_NOTIFIER(MainMethod), 0,
147                        PThread::NoAutoDeleteThread, PThread::NormalPriority, "H.460.24");
148 }
149 
150 
151 
NATTest()152 PSTUNClient::NatTypes PNatMethod_H46024::NATTest()
153 {
154 
155     PSTUNClient::NatTypes testtype;
156     WORD testport;
157 #ifdef H323_H46019M
158     testport = (WORD)feat->GetEndPoint()->GetMultiplexPort()-1;
159 #else
160     PRandom rand;
161     testport = (WORD)rand.Generate(singlePortInfo.basePort , singlePortInfo.maxPort);
162 #endif
163 
164     singlePortInfo.currentPort = testport;
165     PTRACE(4,"Std23\tSTUN Test Port " << singlePortInfo.currentPort+1);
166 
167     testtype = GetNatType(true);
168 
169 #ifdef H323_H46019M
170     // if we have a cone NAT check the RTCP Port to see if not existing binding
171     if (testtype == PSTUNClient::ConeNat || natType == PSTUNClient::UnknownNat) {
172         PThread::Sleep(10);
173         PTRACE(4,"Std23\tCone NAT Detected rechecking. Test Port " << singlePortInfo.currentPort+1);
174         PSTUNClient::NatTypes test2 = GetNatType(true);
175         if (test2 > testtype)
176             testtype = test2;
177     }
178 #endif
179 
180     return testtype;
181 }
182 
183 int recheckTime = 300000;    // 5 minutes
184 
MainMethod(PThread &,H323_INT)185 void PNatMethod_H46024::MainMethod(PThread &,  H323_INT)
186 {
187 
188     while (natType == PSTUNClient::UnknownNat ||
189                 natType == PSTUNClient::ConeNat) {
190         PSTUNClient::NatTypes testtype = NATTest();
191         if (natType != testtype) {
192             natType = testtype;
193             PIPSocket::Address extIP;
194             if (GetExternalAddress(extIP)) {
195                 feat->GetEndPoint()->NATMethodCallBack(GetName(),2,natType);
196                 feat->OnNATTypeDetection(natType, extIP);
197             }
198         }
199 
200         if (natType == PSTUNClient::ConeNat) {
201             isAvailable = true;
202             PThread::Sleep(recheckTime);
203             continue;
204         }
205 
206         isAvailable = false;
207         if (natType == PSTUNClient::UnknownNat) {
208             PTRACE(1,"Std24\tNAT Test failed to resolve NAT Type");
209             break;
210         }
211     }
212 
213 }
214 
IsAvailable(const PIPSocket::Address &)215 bool PNatMethod_H46024::IsAvailable(const PIPSocket::Address & /*binding*/)
216 {
217     if (!isActive)
218         return false;
219 
220     return isAvailable;
221 }
222 
SetAvailable()223 void PNatMethod_H46024::SetAvailable()
224 {
225     feat->GetEndPoint()->NATMethodCallBack(GetName(),1,"Available");
226     isAvailable = true;
227 }
228 
Activate(bool act)229 void PNatMethod_H46024::Activate(bool act)
230 {
231     if (act && !isAvailable)  // Special case where activated but not available.
232        isAvailable = true;
233 
234     isActive = act;
235 }
236 
GetNATType()237 PSTUNClient::NatTypes PNatMethod_H46024::GetNATType()
238 {
239     return natType;
240 }
241 
CreateRandomPortPair(unsigned int start,unsigned int end)242 WORD PNatMethod_H46024::CreateRandomPortPair(unsigned int start, unsigned int end)
243 {
244     WORD num;
245     PRandom rand;
246     num = (WORD)rand.Generate(start,end);
247     if (num %2 != 0)
248         num++;  // Make sure the number is even
249 
250     return num;
251 }
252 
253 
CreateSocketPair(PUDPSocket * & socket1,PUDPSocket * & socket2,const PIPSocket::Address & binding,void * userData)254 PBoolean PNatMethod_H46024::CreateSocketPair(PUDPSocket * & socket1,
255                                              PUDPSocket * & socket2,
256                                              const PIPSocket::Address & binding,
257 	                                         void * userData
258                                              )
259 {
260     PWaitAndSignal m(portMute);
261 
262     H323Connection::SessionInformation * info = (H323Connection::SessionInformation *)userData;
263 #ifdef H323_H46019M
264     PNatMethod_H46019 * handler =
265                (PNatMethod_H46019 *)feat->GetEndPoint()->GetNatMethods().GetMethodByName("H46019");
266 
267     if (handler && info && (info->GetRecvMultiplexID() > 0)) {
268         if (!handler->IsMultiplexed()) {
269            // Set Multiplex ports here
270            SetPortRanges(multiplexPorts.basePort, multiplexPorts.maxPort, multiplexPorts.basePort, multiplexPorts.maxPort);
271 
272            H46019MultiplexSocket * & muxSocket1 = (H46019MultiplexSocket * &)handler->GetMultiplexSocket(true);
273            H46019MultiplexSocket * & muxSocket2 = (H46019MultiplexSocket * &)handler->GetMultiplexSocket(false);
274            muxSocket1 = new H46019MultiplexSocket(true);
275            muxSocket2 = new H46019MultiplexSocket(false);
276            pairedPortInfo.currentPort = feat->GetEndPoint()->GetMultiplexPort()-1;
277 
278 #if PTLIB_VER >= 2130
279            if (!PSTUNClient::CreateSocketPair(muxSocket1->GetSubSocket(), muxSocket2->GetSubSocket(), binding, (PObject *)1))
280 #else
281            if (!PSTUNClient::CreateSocketPair(muxSocket1->GetSubSocket(), muxSocket2->GetSubSocket(), binding))
282 #endif
283                 return false;
284 
285            PIPSocket::Address stunAddress;
286            muxSocket1->GetSubSocket()->GetLocalAddress(stunAddress);
287            PTRACE(1,"Std24\tMux STUN Created: " << stunAddress  << " "
288                         << muxSocket1->GetSubSocket()->GetPort() << "-" << muxSocket2->GetSubSocket()->GetPort());
289 
290            handler->StartMultiplexListener();  // Start Multiplexing Listening thread;
291            handler->EnableMultiplex(true);
292         }
293 
294        socket1 = new H46019UDPSocket(*handler->GetHandler(),info,true);      /// Data
295        socket2 = new H46019UDPSocket(*handler->GetHandler(),info,false);     /// Signal
296 
297        PNatMethod_H46019::RegisterSocket(true ,info->GetRecvMultiplexID(), socket1);
298        PNatMethod_H46019::RegisterSocket(false,info->GetRecvMultiplexID(), socket2);
299 
300     } else {
301         // Set standard ports here
302         SetPortRanges(standardPorts.basePort, standardPorts.maxPort, standardPorts.basePort, standardPorts.maxPort);
303 #else
304     {
305 #endif
306 
307 #if PTLIB_VER >= 2130
308         if (!PSTUNClient::CreateSocketPair(socket1,socket2,binding, NULL))
309 #else
310         if (!PSTUNClient::CreateSocketPair(socket1,socket2,binding))
311 #endif
312              return false;
313     }
314 
315     SetConnectionSockets(socket1, socket2, info);
316     return true;
317 }
318 
319 
320 void PNatMethod_H46024::SetConnectionSockets(PUDPSocket * data, PUDPSocket * control,
321                                              H323Connection::SessionInformation * info)
322 {
323     if (info != NULL) {
324         H323Connection * connection = PRemoveConst(H323Connection, info->GetConnection());
325         if (connection != NULL)
326             connection->SetRTPNAT(info->GetSessionID(), data,control);
327     }
328 }
329 
330 //////////////////////////////////////////////////////////////////////
331 
332 H460_FEATURE(Std23);
333 
334 H460_FeatureStd23::H460_FeatureStd23()
335 : H460_FeatureStd(23)
336 {
337   PTRACE(6,"Std23\tInstance Created");
338 
339   FeatureCategory = FeatureSupported;
340 
341   EP = NULL;
342   alg = false;
343   isavailable = true;
344   isEnabled = false;
345   natType = PSTUNClient::UnknownNat;
346   externalIP = PIPSocket::GetDefaultIpAny();
347   useAlternate = 0;
348   natNotify = false;
349 
350 }
351 
352 H460_FeatureStd23::~H460_FeatureStd23()
353 {
354 }
355 
356 void H460_FeatureStd23::AttachEndPoint(H323EndPoint * _ep)
357 {
358     EP = _ep;
359     isEnabled = EP->H46023IsEnabled();
360 
361     // Ignore if already manually using STUN
362     isavailable = (EP->GetSTUN() == NULL);
363 }
364 
365 PBoolean H460_FeatureStd23::OnSendGatekeeperRequest(H225_FeatureDescriptor & pdu)
366 {
367     if (!isEnabled)
368         return false;
369 
370     if (!isavailable)
371         return FALSE;
372 
373     H460_FeatureStd feat = H460_FeatureStd(23);
374     pdu = feat;
375     return TRUE;
376 }
377 
378 PBoolean H460_FeatureStd23::OnSendRegistrationRequest(H225_FeatureDescriptor & pdu)
379 {
380     if (!isEnabled)
381         return false;
382 
383     if (!isavailable)
384             return FALSE;
385 
386     // Build Message
387     H460_FeatureStd feat = H460_FeatureStd(23);
388 
389     if ((EP->GetGatekeeper() == NULL) ||
390         (!EP->GetGatekeeper()->IsRegistered())) {
391           // First time registering
392           // We always support remote
393             feat.Add(remoteNATOID,H460_FeatureContent(true));
394 #ifdef H323_H46024A
395             feat.Add(AnnexAOID,H460_FeatureContent(true));
396 #endif
397 #ifdef H323_H46024B
398             feat.Add(AnnexBOID,H460_FeatureContent(true));
399 #endif
400     } else {
401         if (alg) {
402               // We should be disabling H.460.23/.24 support but
403               // we will disable H.460.18/.19 instead :) and say we have no NAT..
404                 feat.Add(NATTypeOID,H460_FeatureContent(1,8));
405                 feat.Add(remoteNATOID,H460_FeatureContent(false));
406                 isavailable = false;
407                 alg = false;
408         } else {
409             if (natNotify || AlternateNATMethod()) {
410                 feat.Add(NATTypeOID,H460_FeatureContent(natType,8));
411                 natNotify = false;
412             }
413         }
414     }
415 
416     pdu = feat;
417 
418     return true;
419 }
420 
421 void H460_FeatureStd23::OnReceiveGatekeeperConfirm(const H225_FeatureDescriptor & /*pdu*/)
422 {
423     isEnabled = true;
424 }
425 
426 void H460_FeatureStd23::OnReceiveRegistrationConfirm(const H225_FeatureDescriptor & pdu)
427 {
428     isEnabled = true;
429 
430    H460_FeatureStd & feat = (H460_FeatureStd &)pdu;
431 
432    // Ignore whether the gatekeeper detected as being behind NAT
433    // The STUN test will confirm - SH
434    //PBoolean NATdetect = false;
435    //if (feat.Contains(localNATOID))
436    //    NATdetect = feat.Value(localNATOID);
437 
438        if (feat.Contains(STUNServOID)) {
439           H323TransportAddress addr = feat.Value(STUNServOID);
440           StartSTUNTest(addr.Mid(3));
441        }
442 
443        if (feat.Contains(NATDetRASOID)) {
444            H323TransportAddress addr = feat.Value(NATDetRASOID);
445            PIPSocket::Address ip;
446            addr.GetIpAddress(ip);
447            if (DetectALG(ip)) {
448              // if we have an ALG then to be on the safe side
449              // disable .23/.24 so that the ALG has more chance
450              // of behaving properly...
451                alg = true;
452                DelayedReRegistration();
453            }
454        }
455 }
456 
457 
458 void H460_FeatureStd23::OnNATTypeDetection(PSTUNClient::NatTypes type, const PIPSocket::Address & ExtIP)
459 {
460     if (natType == type)
461         return;
462 
463     externalIP = ExtIP;
464 
465     if (natType == PSTUNClient::UnknownNat) {
466         PTRACE(4,"Std23\tSTUN Test Result: " << type << " forcing reregistration.");
467 #ifdef H323_UPnP
468         if (type > PSTUNClient::ConeNat) {
469            PString name = PString();
470            if (IsAlternateAvailable(name))
471                EP->NATMethodCallBack(name,1,"Available");
472            else
473                EP->InitialiseUPnP();
474         }
475 #endif
476         natType = type;  // first time detection
477     } else {
478         PTRACE(2,"Std23\tBAD NAT Detected: Was " << natType << " Now " << type << " Disabling H.460.23/.24");
479         natType = PSTUNClient::UnknownNat;  // Leopard changed it spots (disable H.460.23/.24)
480     }
481 
482     natNotify = true;
483     EP->ForceGatekeeperReRegistration();
484 }
485 
486 bool H460_FeatureStd23::DetectALG(const PIPSocket::Address & detectAddress)
487 {
488 
489 #ifdef H323_IPV6
490   // Again horrible code should be able to get interface listing for a given protocol - SH
491   PBoolean ipv6IPv4Discover = false;
492   if (detectAddress.GetVersion() == 4 && PIPSocket::GetDefaultIpAddressFamily() == AF_INET6) {
493       PIPSocket::SetDefaultIpAddressFamilyV4();
494       ipv6IPv4Discover = true;
495   }
496 #endif
497     bool found = true;
498     PIPSocket::InterfaceTable if_table;
499     if (!PIPSocket::GetInterfaceTable(if_table)) {
500         PTRACE(1, "Std23\tERROR: Can't get interface table");
501         found = false;
502     } else {
503         for (PINDEX i=0; i< if_table.GetSize(); i++) {
504             if (detectAddress == if_table[i].GetAddress()) {
505                 PTRACE(4, "Std23\tNo Intermediary device detected between EP and GK");
506                 found = false;
507                 break;
508             }
509         }
510     }
511 #ifdef H323_IPV6
512   if (ipv6IPv4Discover)
513       PIPSocket::SetDefaultIpAddressFamilyV6();
514 #endif
515     if (found) {
516         PTRACE(4, "Std23\tWARNING: Intermediary device detected!");
517         EP->NATMethodCallBack("ALG",1,"Available");
518         return true;
519     }
520 
521     return false;
522 }
523 
524 void H460_FeatureStd23::StartSTUNTest(const PString & server)
525 {
526     PString s;
527 #ifdef P_DNS
528     PStringList SRVs;
529     PStringList x = server.Tokenise(":");
530     PString number = "h323:user@" + x[0];
531     if (PDNS::LookupSRV(number,"_stun._udp.",SRVs))
532         s = SRVs[0];
533     else
534 #endif
535         s = server;
536 
537     // Remove any previous NAT methods.
538     EP->GetNatMethods().RemoveMethod("H46024");
539     natType = PSTUNClient::UnknownNat;
540 
541     PNatMethod_H46024 * xnat = (PNatMethod_H46024 *)EP->GetNatMethods().LoadNatMethod("H46024");
542 #ifdef H323_UPnP
543     PString name = PString();
544     if (IsAlternateAvailable(name)) {
545           EP->NATMethodCallBack(name,1,"Available");
546           EP->ForceGatekeeperReRegistration();
547     } else
548 #endif
549         xnat->Start(s,this);
550 
551     EP->GetNatMethods().AddMethod(xnat);
552 }
553 
554 bool H460_FeatureStd23::IsAvailable()
555 {
556     return isEnabled;
557 }
558 
559 #if H323_UPnP
560 bool H460_FeatureStd23::IsAlternateAvailable(PString & name)
561 {
562     PNatMethod_UPnP * upnpMethod = (PNatMethod_UPnP *)EP->GetNatMethods().GetMethodByName("UPnP");
563     if (upnpMethod && upnpMethod->IsAvailable(PIPSocket::Address::GetAny(4))) {
564           PTRACE(4,"Std23\tSTUN Setting alternate: UPnP");
565           name = upnpMethod->GetName();
566           natType = PSTUNClient::ConeNat;
567           natNotify = true;
568           useAlternate = 1;
569           upnpMethod->Activate(true);
570           return true;
571     }
572     return false;
573 }
574 #endif
575 
576 void H460_FeatureStd23::DelayedReRegistration()
577 {
578     PThread::Sleep(1000);
579     EP->ForceGatekeeperReRegistration();  // We have an ALG so notify the gatekeeper
580 }
581 
582 bool H460_FeatureStd23::AlternateNATMethod()
583 {
584 #ifdef H323_UPnP
585     if (natType <= PSTUNClient::ConeNat || useAlternate > 0)
586         return false;
587 
588     H323NatList & natlist = EP->GetNatMethods().GetNATList();
589 
590     for (PINDEX i=0; i< natlist.GetSize(); i++) {
591 #if PTLIB_VER >= 2130
592         PString methName = natlist[i].GetMethodName();
593 #else
594         PString methName = natlist[i].GetName();
595 #endif
596         if (methName == "UPnP" &&
597             natlist[i].GetRTPSupport() == PSTUNClient::RTPSupported) {
598             PIPSocket::Address extIP;
599             natlist[i].GetExternalAddress(extIP);
600             if (extIP.IsAny() || !extIP.IsValid() || externalIP.IsLoopback() || extIP == externalIP) {
601                 PTRACE(4,"H46023\tUPnP Change NAT from " << natType << " to " << PSTUNClient::ConeNat);
602                 natType = PSTUNClient::ConeNat;
603                 useAlternate = 1;
604                 natlist[i].Activate(true);
605                 EP->NATMethodCallBack(methName,1,"Available");
606                 return true;
607             } else {
608                 PTRACE(4,"H46023\tUPnP Unavailable subNAT STUN: " << externalIP << " UPnP " << extIP);
609                 useAlternate = 2;
610             }
611         }
612     }
613 #endif
614     return false;
615 }
616 
617 bool H460_FeatureStd23::UseAlternate()
618 {
619     return (useAlternate == 1);
620 }
621 
622 bool H460_FeatureStd23::IsUDPAvailable()
623 {
624     return (natType < PSTUNClient::BlockedNat);
625 }
626 
627 ///////////////////////////////////////////////////////////////////
628 
629 
630 H460_FEATURE(Std24);
631 
632 H460_FeatureStd24::H460_FeatureStd24()
633 : H460_FeatureStd(24),
634   EP(NULL), CON(NULL), natconfig(H460_FeatureStd24::e_unknown),
635   isEnabled(false), useAlternate(false)
636 {
637  PTRACE(6,"Std24\tInstance Created");
638 
639  FeatureCategory = FeatureSupported;
640 }
641 
642 H460_FeatureStd24::~H460_FeatureStd24()
643 {
644 }
645 
646 void H460_FeatureStd24::AttachEndPoint(H323EndPoint * _ep)
647 {
648    EP = _ep;
649     // We only enable IF the gatekeeper supports H.460.23
650     H460_FeatureSet * gkfeat = EP->GetGatekeeperFeatures();
651     if (gkfeat && gkfeat->HasFeature(23)) {
652         H460_FeatureStd23 * feat = (H460_FeatureStd23 *)gkfeat->GetFeature(23);
653         isEnabled = feat->IsAvailable();
654         useAlternate = feat->UseAlternate();
655     } else {
656         PTRACE(4,"Std24\tH.460.24 disabled as H.460.23 is disabled!");
657         isEnabled = false;
658     }
659 }
660 
661 void H460_FeatureStd24::AttachConnection(H323Connection * _conn)
662 {
663    CON = _conn;
664 }
665 
666 PBoolean H460_FeatureStd24::OnSendAdmissionRequest(H225_FeatureDescriptor & pdu)
667 {
668     // Ignore if already not enabled or manually using STUN
669     if (!isEnabled)
670         return FALSE;
671 
672 #ifdef H323_H46023
673     if (!EP->H46023NatMethodSelection(GetFeatureName()[0]))
674         return false;
675 #endif
676 
677     PWaitAndSignal m(h460mute);
678 
679     // Build Message
680     PTRACE(6,"Std24\tSending ARQ ");
681     H460_FeatureStd feat = H460_FeatureStd(24);
682 
683     if (natconfig != e_unknown) {
684        feat.Add(NATInstOID,H460_FeatureContent((unsigned)natconfig,8));
685     }
686 
687     pdu = feat;
688     return TRUE;
689 }
690 
691 void H460_FeatureStd24::OnReceiveAdmissionConfirm(const H225_FeatureDescriptor & pdu)
692 {
693      H460_FeatureStd & feat = (H460_FeatureStd &)pdu;
694 
695     if (feat.Contains(NATInstOID)) {
696         PTRACE(6,"Std24\tReading ACF");
697         unsigned NATinst = feat.Value(NATInstOID);
698         natconfig = (NatInstruct)NATinst;
699         HandleNATInstruction(natconfig);
700     }
701 }
702 
703 void H460_FeatureStd24::OnReceiveAdmissionReject(const H225_FeatureDescriptor & pdu)
704 {
705      PTRACE(6,"Std24\tARJ Received");
706      HandleNATInstruction(H460_FeatureStd24::e_natFailure);
707 }
708 
709 PBoolean H460_FeatureStd24::OnSendSetup_UUIE(H225_FeatureDescriptor & pdu)
710 {
711   // Ignore if already not enabled or manually using STUN
712   if (!isEnabled)
713         return FALSE;
714 
715  PTRACE(6,"Std24\tSend Setup");
716     if (natconfig == H460_FeatureStd24::e_unknown)
717         return FALSE;
718 
719     H460_FeatureStd feat = H460_FeatureStd(24);
720 
721     int remoteconfig;
722     switch (natconfig) {
723         case H460_FeatureStd24::e_noassist:
724             remoteconfig = H460_FeatureStd24::e_noassist;
725             break;
726         case H460_FeatureStd24::e_localMaster:
727             remoteconfig = H460_FeatureStd24::e_remoteMaster;
728             break;
729         case H460_FeatureStd24::e_remoteMaster:
730             remoteconfig = H460_FeatureStd24::e_localMaster;
731             break;
732         case H460_FeatureStd24::e_localProxy:
733             remoteconfig = H460_FeatureStd24::e_remoteProxy;
734             break;
735         case H460_FeatureStd24::e_remoteProxy:
736             remoteconfig = H460_FeatureStd24::e_localProxy;
737             break;
738         default:
739             remoteconfig = natconfig;
740     }
741 
742     feat.Add(NATInstOID,H460_FeatureContent(remoteconfig,8));
743     pdu = feat;
744     return TRUE;
745 }
746 
747 void H460_FeatureStd24::OnReceiveSetup_UUIE(const H225_FeatureDescriptor & pdu)
748 {
749 
750     PWaitAndSignal m(h460mute);
751 
752      H460_FeatureStd & feat = (H460_FeatureStd &)pdu;
753 
754      if (feat.Contains(NATInstOID)) {
755       PTRACE(6,"Std24\tReceive Setup");
756 
757         unsigned NATinst = feat.Value(NATInstOID);
758         natconfig = (NatInstruct)NATinst;
759         HandleNATInstruction(natconfig);
760     }
761 
762 }
763 
764 void H460_FeatureStd24::HandleNATInstruction(NatInstruct _config)
765 {
766 
767         PTRACE(4,"Std24\tNAT Instruction Received: " << _config);
768         switch (_config) {
769             case H460_FeatureStd24::e_localMaster:
770                 PTRACE(4,"Std24\tLocal NAT Support: H.460.24 ENABLED");
771                 CON->SetRemoteNAT(true);
772                 CON->H46019SetOffload();
773                 SetNATMethods(e_enable);
774                 break;
775 
776             case H460_FeatureStd24::e_remoteMaster:
777                 PTRACE(4,"Std24\tRemote NAT Support: ALL NAT DISABLED");
778                 CON->H46019SetOffload();
779                 if (IsNatSendAvailable()) {  // If we can use STUN do it!
780                     CON->SetRemoteNAT(false);
781                     SetNATMethods(e_enable);
782                 } else
783                     SetNATMethods(e_disable);
784                 break;
785 
786             case H460_FeatureStd24::e_remoteProxy:
787                 PTRACE(4,"Std24\tRemote Proxy Support: H.460.24 DISABLED");
788                 SetNATMethods(e_default);
789                 break;
790 
791             case H460_FeatureStd24::e_localProxy:
792                 PTRACE(4,"Std24\tCall Local Proxy: H.460.24 DISABLED");
793                 SetNATMethods(e_default);
794                 break;
795 
796 #ifdef H323_H46024A
797             case H460_FeatureStd24::e_natAnnexA:
798                 PTRACE(4,"Std24\tSame NAT: H.460.24 AnnexA ENABLED");
799                 CON->H46024AEnabled();
800                 SetNATMethods(e_AnnexA);
801                 break;
802 #endif
803 
804 #ifdef H323_H46024B
805             case H460_FeatureStd24::e_natAnnexB:
806                 PTRACE(4,"Std24\tSame NAT: H.460.24 AnnexA ENABLED");
807                 CON->H46024BEnabled();
808                 //CON->H46024AEnabled();  // Might be on same internal network
809                 SetNATMethods(e_AnnexB);
810                 break;
811 #endif
812 
813             case H460_FeatureStd24::e_natFailure:
814                 PTRACE(4,"Std24\tCall Failure Detected");
815                 EP->FeatureCallBack(GetFeatureName()[0],1,"Call Failure");
816                 break;
817             case H460_FeatureStd24::e_noassist:
818                 PTRACE(4,"Std24\tNAT Call direct");
819 				// fallthrough intended
820             default:
821                 PTRACE(4,"Std24\tNo Assist: H.460.24 DISABLED.");
822                 CON->DisableNATSupport();
823                 SetNATMethods(e_default);
824                 break;
825         }
826 }
827 
828 void H460_FeatureStd24::SetH46019State(bool state)
829 {
830 #ifdef H323_H46018
831     if (CON->GetFeatureSet()->HasFeature(19)) {
832         H460_Feature * feat = CON->GetFeatureSet()->GetFeature(19);
833 
834         PTRACE(4,"H46024\t" << (state ? "En" : "Dis") << "abling H.460.19 support for call");
835         ((H460_FeatureStd19 *)feat)->SetAvailable(state);
836     }
837 #endif
838 }
839 
840 PBoolean H460_FeatureStd24::IsNatSendAvailable()
841 {
842    H323NatList & natlist = EP->GetNatMethods().GetNATList();
843 
844    PBoolean available = false;
845    PINDEX i=0;
846    for (i=0; i< natlist.GetSize(); i++) {
847 #if PTLIB_VER >= 2130
848         if (natlist[i].GetMethodName() == "H46024") break;
849 #else
850         if (natlist[i].GetName() == "H46024") break;
851 #endif
852    }
853    if (i < natlist.GetSize()) {
854      PNatMethod_H46024 & meth = (PNatMethod_H46024 &)natlist[i];
855       switch (meth.GetNatType(false)) {
856         case PSTUNClient::ConeNat :
857         case PSTUNClient::RestrictedNat :
858         case PSTUNClient::PortRestrictedNat :
859           available = true;
860           break;
861         case PSTUNClient::SymmetricNat :
862         default :   // UnknownNet, SymmetricFirewall, BlockedNat
863           break;
864       }
865    }
866    return available;
867 }
868 
869 void H460_FeatureStd24::SetNATMethods(H46024NAT state)
870 {
871 
872     H323NatList & natlist = EP->GetNatMethods().GetNATList();
873 
874     for (PINDEX i=0; i< natlist.GetSize(); i++) {
875 #if PTLIB_VER >= 2130
876         PString name = natlist[i].GetMethodName();
877 #else
878         PString name = natlist[i].GetName();
879 #endif
880         switch (state) {
881             case H460_FeatureStd24::e_AnnexA:   // To do Annex A Implementation.
882             case H460_FeatureStd24::e_AnnexB:   // To do Annex B Implementation.
883             case H460_FeatureStd24::e_default:
884                 if (name == "H46024" || name == "UPnP")
885                     natlist[i].Activate(false);
886                 break;
887             case H460_FeatureStd24::e_enable:
888                 if (name == "H46024" && !useAlternate)
889                     natlist[i].Activate(true);
890                 else if (name == "UPnP" && useAlternate)
891                     natlist[i].Activate(true);
892                 else
893                     natlist[i].Activate(false);
894                 break;
895             case H460_FeatureStd24::e_disable:
896                 if (name == "H46019" && CON->IsH46019Multiplexed())
897                     natlist[i].Activate(true);
898                 else
899                     natlist[i].Activate(false);
900                 break;
901             default:
902                 break;
903         }
904     }
905 
906    PTRACE(6,"Std24\tNAT Methods " << GetH460NATString(state));
907    for (PINDEX i=0; i< natlist.GetSize(); i++) {
908 #if PTLIB_VER >= 2130
909        PString name = natlist[i].GetMethodName();
910 #else
911        PString name = natlist[i].GetName();
912 #endif
913        PTRACE(6, "H323\tNAT Method " << i << " " << name << " Ready: "
914                   << (natlist[i].IsAvailable(PIPSocket::Address::GetAny(4)) ? "Yes" : "No"));
915    }
916 }
917 
918 PString H460_FeatureStd24::GetNATStrategyString(NatInstruct method)
919 {
920   static const char * const Names[10] = {
921         "Unknown Strategy",
922         "No Assistance",
923         "Local Master",
924         "Remote Master",
925         "Local Proxy",
926         "Remote Proxy",
927         "Full Proxy",
928         "AnnexA SameNAT",
929         "AnnexB NAToffload",
930         "Call Failure!"
931   };
932 
933   if (method < 10)
934     return Names[method];
935 
936    return psprintf("<NAT Strategy %u>", method);
937 }
938 
939 PString H460_FeatureStd24::GetH460NATString(H46024NAT method)
940 {
941     static const char * const Names[5] = {
942         "default"
943         "enable"
944         "AnnexA"
945         "AnnexB"
946         "disable"
947     };
948 
949   if (method < 5)
950     return Names[method];
951 
952    return psprintf("<H460NAT %u>", method);
953 }
954 
955 
956 
957 #ifdef _MSC_VER
958 #pragma warning(default : 4239)
959 #endif
960 
961 #endif // Win32_WCE
962