6 * Portable Windows Library
8 * Copyright (c) 2003 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (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/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 1.26 2007/09/08 11:34:28 rjongbloed
28 * Improved memory checking (leaks etc), especially when using MSVC debug library.
30 * Revision 1.25 2007/08/22 05:04:39 rjongbloed
31 * Added ability to set a specific local port for STUN created sockets.
33 * Revision 1.24 2007/07/22 03:07:31 rjongbloed
34 * Added parameter so can bind STUN socket to specific interface.
36 * Revision 1.23 2007/02/11 13:13:07 shorne
37 * Added message when stun server cannot be reached
39 * Revision 1.22 2006/12/23 15:08:11 shorne
40 * Now Factory loaded for ease of addition of new NAT Methods
42 * Revision 1.21 2006/08/29 18:41:20 dsandras
43 * Check validity of STUN messages.
45 * Revision 1.20 2005/12/05 21:58:36 dsandras
46 * Fixed bug when looking if the cache is still valid.
48 * Revision 1.19 2005/11/30 12:47:41 csoutheren
49 * Removed tabs, reformatted some code, and changed tags for Doxygen
51 * Revision 1.18 2005/07/13 11:15:26 csoutheren
52 * Backported NAT abstraction files from isvo branch
54 * Revision 1.17 2005/06/20 10:55:17 rjongbloed
55 * Changed the timeout and retries so if there is a blocking firewall it does not take 15 seconds to find out!
56 * Added access functions so timeout and retries are application configurable.
57 * Added function (and << operator) to get NAT type enum as string.
59 * Revision 1.16.4.1 2005/04/25 13:19:27 shorne
60 * Add Support for other NAT methods
62 * Revision 1.16 2004/11/25 07:23:46 csoutheren
63 * Added IsSupportingRTP function to simplify detecting when STUN supports RTP
65 * Revision 1.15 2004/10/26 05:58:23 csoutheren
66 * Increased timeout on STUN responses to avoid spurious STUN failures due
67 * to network trsffic/congestion etc
69 * Revision 1.14 2004/08/18 13:16:07 rjongbloed
70 * Fixed STUN CreateSocketPair so first socket is always even.
72 * Revision 1.13 2004/03/14 05:47:52 rjongbloed
73 * Fixed incorrect detection of symmetric NAT (eg Linux masquerading) and also
74 * some NAT systems which are partially blocked due to firewall rules.
76 * Revision 1.12 2004/02/24 11:15:48 rjongbloed
77 * Added function to get external router address, also did a bunch of documentation.
79 * Revision 1.11 2004/02/17 11:11:05 rjongbloed
80 * Added missing #pragma pack() to turn off byte alignment for the last class, thanks Ted Szoczei
82 * Revision 1.10 2004/01/17 17:54:02 rjongbloed
83 * Added function to get server name from STUN client.
85 * Revision 1.9 2003/10/08 22:00:18 dereksmithies
86 * Fix unsigned/signed warning message. Thanks to Craig Southeren.
88 * Revision 1.8 2003/10/05 00:56:25 rjongbloed
89 * Rewrite of STUN to not to use imported code with undesirable license.
91 * Revision 1.5 2003/02/05 06:26:49 robertj
92 * More work in making the STUN usable for Symmetric NAT systems.
94 * Revision 1.4 2003/02/04 07:02:17 robertj
95 * Added ip/port version of constructor.
96 * Removed creating sockets for Open type.
98 * Revision 1.3 2003/02/04 05:55:04 craigs
99 * Added socket pair function
101 * Revision 1.2 2003/02/04 05:06:24 craigs
102 * Added new functions
104 * Revision 1.1 2003/02/04 03:31:04 robertj
110 #pragma implementation "pstun.h"
113 #define P_FORCE_STATIC_PLUGIN
116 #include <ptclib/pstun.h>
117 #include <ptclib/random.h>
122 #if defined(_WIN32) && !defined(P_FORCE_STATIC_PLUGIN)
123 #error "pstun.cxx must be compiled without precompiled headers"
126 PCREATE_NAT_PLUGIN(STUN
);
128 // Sample server is at larry.gloo.net
130 #define DEFAULT_REPLY_TIMEOUT 1000
131 #define DEFAULT_POLL_RETRIES 5
132 #define DEFAULT_NUM_SOCKETS_FOR_PAIRING 4
135 ///////////////////////////////////////////////////////////////////////
137 PSTUNClient::PSTUNClient()
139 serverPort(DefaultPort
),
140 replyTimeout(DEFAULT_REPLY_TIMEOUT
),
141 pollRetries(DEFAULT_POLL_RETRIES
),
142 numSocketsForPairing(DEFAULT_NUM_SOCKETS_FOR_PAIRING
),
144 cachedExternalAddress(0),
145 timeAddressObtained(0)
149 PSTUNClient::PSTUNClient(const PString
& server
,
150 WORD portBase
, WORD portMax
,
151 WORD portPairBase
, WORD portPairMax
)
153 serverPort(DefaultPort
),
154 replyTimeout(DEFAULT_REPLY_TIMEOUT
),
155 pollRetries(DEFAULT_POLL_RETRIES
),
156 numSocketsForPairing(DEFAULT_NUM_SOCKETS_FOR_PAIRING
),
158 cachedExternalAddress(0),
159 timeAddressObtained(0)
162 SetPortRanges(portBase
, portMax
, portPairBase
, portPairMax
);
166 PSTUNClient::PSTUNClient(const PIPSocket::Address
& address
, WORD port
,
167 WORD portBase
, WORD portMax
,
168 WORD portPairBase
, WORD portPairMax
)
169 : serverAddress(address
),
171 replyTimeout(DEFAULT_REPLY_TIMEOUT
),
172 pollRetries(DEFAULT_POLL_RETRIES
),
173 numSocketsForPairing(DEFAULT_NUM_SOCKETS_FOR_PAIRING
),
175 cachedExternalAddress(0),
176 timeAddressObtained(0)
178 SetPortRanges(portBase
, portMax
, portPairBase
, portPairMax
);
181 void PSTUNClient::Initialise(const PString
& server
,
182 WORD portBase
, WORD portMax
,
183 WORD portPairBase
, WORD portPairMax
)
186 SetPortRanges(portBase
, portMax
, portPairBase
, portPairMax
);
190 PString
PSTUNClient::GetServer() const
193 str
<< serverAddress
<< ':' << serverPort
;
198 BOOL
PSTUNClient::SetServer(const PString
& server
)
200 PINDEX colon
= server
.Find(':');
201 if (colon
== P_MAX_INDEX
) {
202 if (!PIPSocket::GetHostAddress(server
, serverAddress
))
206 if (!PIPSocket::GetHostAddress(server
.Left(colon
), serverAddress
))
208 serverPort
= PIPSocket::GetPortByService("udp", server
.Mid(colon
+1));
211 return serverAddress
.IsValid() && serverPort
!= 0;
215 BOOL
PSTUNClient::SetServer(const PIPSocket::Address
& address
, WORD port
)
217 serverAddress
= address
;
219 return serverAddress
.IsValid() && serverPort
!= 0;
224 struct PSTUNAttribute
227 MAPPED_ADDRESS
= 0x0001,
228 RESPONSE_ADDRESS
= 0x0002,
229 CHANGE_REQUEST
= 0x0003,
230 SOURCE_ADDRESS
= 0x0004,
231 CHANGED_ADDRESS
= 0x0005,
234 MESSAGE_INTEGRITY
= 0x0008,
236 UNKNOWN_ATTRIBUTES
= 0x000a,
237 REFLECTED_FROM
= 0x000b,
244 PSTUNAttribute
* GetNext() const { return (PSTUNAttribute
*)(((const BYTE
*)this)+length
+4); }
247 class PSTUNAddressAttribute
: public PSTUNAttribute
255 PIPSocket::Address
GetIP() const { return PIPSocket::Address(4, ip
); }
258 enum { SizeofAddressAttribute
= sizeof(BYTE
)+sizeof(BYTE
)+sizeof(WORD
)+sizeof(PIPSocket::Address
) };
259 void InitAddrAttr(Types newType
)
261 type
= (WORD
)newType
;
262 length
= SizeofAddressAttribute
;
266 bool IsValidAddrAttr(Types checkType
) const
268 return type
== checkType
&& length
== SizeofAddressAttribute
;
272 class PSTUNMappedAddress
: public PSTUNAddressAttribute
275 void Initialise() { InitAddrAttr(MAPPED_ADDRESS
); }
276 bool IsValid() const { return IsValidAddrAttr(MAPPED_ADDRESS
); }
279 class PSTUNChangedAddress
: public PSTUNAddressAttribute
282 void Initialise() { InitAddrAttr(CHANGED_ADDRESS
); }
283 bool IsValid() const { return IsValidAddrAttr(CHANGED_ADDRESS
); }
286 class PSTUNChangeRequest
: public PSTUNAttribute
291 PSTUNChangeRequest() { }
293 PSTUNChangeRequest(bool changeIP
, bool changePort
)
296 SetChangeIP(changeIP
);
297 SetChangePort(changePort
);
302 type
= CHANGE_REQUEST
;
303 length
= sizeof(flags
);
304 memset(flags
, 0, sizeof(flags
));
306 bool IsValid() const { return type
== CHANGE_REQUEST
&& length
== sizeof(flags
); }
308 bool GetChangeIP() const { return (flags
[3]&4) != 0; }
309 void SetChangeIP(bool on
) { if (on
) flags
[3] |= 4; else flags
[3] &= ~4; }
311 bool GetChangePort() const { return (flags
[3]&2) != 0; }
312 void SetChangePort(bool on
) { if (on
) flags
[3] |= 2; else flags
[3] &= ~2; }
315 class PSTUNMessageIntegrity
: public PSTUNAttribute
322 type
= MESSAGE_INTEGRITY
;
323 length
= sizeof(hmac
);
324 memset(hmac
, 0, sizeof(hmac
));
326 bool IsValid() const { return type
== MESSAGE_INTEGRITY
&& length
== sizeof(hmac
); }
329 struct PSTUNMessageHeader
333 BYTE transactionId
[16];
340 class PSTUNMessage
: public PBYTEArray
344 BindingRequest
= 0x0001,
345 BindingResponse
= 0x0101,
346 BindingError
= 0x0111,
348 SharedSecretRequest
= 0x0002,
349 SharedSecretResponse
= 0x0102,
350 SharedSecretError
= 0x0112,
356 PSTUNMessage(MsgType newType
, const BYTE
* id
= NULL
)
357 : PBYTEArray(sizeof(PSTUNMessageHeader
))
359 SetType(newType
, id
);
362 void SetType(MsgType newType
, const BYTE
* id
= NULL
)
364 SetMinSize(sizeof(PSTUNMessageHeader
));
365 PSTUNMessageHeader
* hdr
= (PSTUNMessageHeader
*)theArray
;
366 hdr
->msgType
= (WORD
)newType
;
367 for (PINDEX i
= 0; i
< ((PINDEX
)sizeof(hdr
->transactionId
)); i
++)
368 hdr
->transactionId
[i
] = id
!= NULL
? id
[i
] : (BYTE
)PRandom::Number();
371 const PSTUNMessageHeader
* operator->() const { return (PSTUNMessageHeader
*)theArray
; }
373 PSTUNAttribute
* GetFirstAttribute() {
375 int length
= ((PSTUNMessageHeader
*)theArray
)->msgLength
;
376 if (theArray
== NULL
|| length
< (int) sizeof(PSTUNMessageHeader
))
379 PSTUNAttribute
* attr
= (PSTUNAttribute
*)(theArray
+sizeof(PSTUNMessageHeader
));
380 PSTUNAttribute
* ptr
= attr
;
382 if (attr
->length
> GetSize() || attr
->type
>= PSTUNAttribute::MaxValidCode
)
385 while (ptr
&& (BYTE
*) ptr
< (BYTE
*)(theArray
+GetSize()) && length
>= (int) ptr
->length
+4) {
387 length
-= ptr
->length
+ 4;
388 ptr
= ptr
->GetNext();
399 int length
= ((PSTUNMessageHeader
*)theArray
)->msgLength
;
400 PSTUNAttribute
* attrib
= GetFirstAttribute();
401 while (attrib
&& length
> 0) {
402 length
-= attrib
->length
+ 4;
403 attrib
= attrib
->GetNext();
406 return length
== 0; // Exactly correct length
409 void AddAttribute(const PSTUNAttribute
& attribute
)
411 PSTUNMessageHeader
* hdr
= (PSTUNMessageHeader
*)theArray
;
412 int oldLength
= hdr
->msgLength
;
413 int attrSize
= attribute
.length
+ 4;
414 int newLength
= oldLength
+ attrSize
;
415 hdr
->msgLength
= (WORD
)newLength
;
416 // hdr pointer may be invalidated by next statement
417 SetMinSize(newLength
+sizeof(PSTUNMessageHeader
));
418 memcpy(theArray
+sizeof(PSTUNMessageHeader
)+oldLength
, &attribute
, attrSize
);
421 void SetAttribute(const PSTUNAttribute
& attribute
)
423 int length
= ((PSTUNMessageHeader
*)theArray
)->msgLength
;
424 PSTUNAttribute
* attrib
= GetFirstAttribute();
426 if (attrib
->type
== attribute
.type
) {
427 if (attrib
->length
== attribute
.length
)
435 length
-= attrib
->length
+ 4;
436 attrib
= attrib
->GetNext();
439 AddAttribute(attribute
);
442 PSTUNAttribute
* FindAttribute(PSTUNAttribute::Types type
)
444 int length
= ((PSTUNMessageHeader
*)theArray
)->msgLength
;
445 PSTUNAttribute
* attrib
= GetFirstAttribute();
447 if (attrib
->type
== type
)
450 length
-= attrib
->length
+ 4;
451 attrib
= attrib
->GetNext();
457 bool Read(PUDPSocket
& socket
)
459 if (!socket
.Read(GetPointer(1000), 1000))
461 SetSize(socket
.GetLastReadCount());
465 bool Write(PUDPSocket
& socket
) const
467 return socket
.Write(theArray
, ((PSTUNMessageHeader
*)theArray
)->msgLength
+sizeof(PSTUNMessageHeader
)) != FALSE
;
470 bool Poll(PUDPSocket
& socket
, const PSTUNMessage
& request
, PINDEX pollRetries
)
472 for (PINDEX retry
= 0; retry
< pollRetries
; retry
++) {
473 if (!request
.Write(socket
))
476 if (Read(socket
) && Validate() &&
477 memcmp(request
->transactionId
, (*this)->transactionId
, sizeof(request
->transactionId
)) == 0)
486 bool PSTUNClient::OpenSocket(PUDPSocket
& socket
, PortInfo
& portInfo
, const PIPSocket::Address
& binding
) const
488 PWaitAndSignal
mutex(portInfo
.mutex
);
490 WORD startPort
= portInfo
.currentPort
;
493 portInfo
.currentPort
++;
494 if (portInfo
.currentPort
> portInfo
.maxPort
)
495 portInfo
.currentPort
= portInfo
.basePort
;
497 if (socket
.Listen(binding
, 1, portInfo
.currentPort
)) {
498 socket
.SetSendAddress(serverAddress
, serverPort
);
499 socket
.SetReadTimeout(replyTimeout
);
503 } while (portInfo
.currentPort
!= startPort
);
505 PTRACE(1, "STUN\tFailed to bind to local UDP port in range "
506 << portInfo
.currentPort
<< '-' << portInfo
.maxPort
);
511 PSTUNClient::NatTypes
PSTUNClient::GetNatType(BOOL force
)
513 if (!force
&& natType
!= UnknownNat
)
517 if (!OpenSocket(socket
, singlePortInfo
, PIPSocket::GetDefaultIpAny()))
518 return natType
= UnknownNat
;
522 /* test I - the client sends a STUN Binding Request to a server, without
523 any flags set in the CHANGE-REQUEST attribute, and without the
524 RESPONSE-ADDRESS attribute. This causes the server to send the response
525 back to the address and port that the request came from. */
526 PSTUNMessage
requestI(PSTUNMessage::BindingRequest
);
527 requestI
.AddAttribute(PSTUNChangeRequest(false, false));
528 PSTUNMessage responseI
;
529 if (!responseI
.Poll(socket
, requestI
, pollRetries
)) {
530 if (socket
.GetErrorCode(PChannel::LastWriteError
) != PChannel::NoError
) {
531 PTRACE(1, "STUN\tError writing to server " << serverAddress
<< ':' << serverPort
<< " - " << socket
.GetErrorText(PChannel::LastWriteError
));
532 return natType
= UnknownNat
; // No response usually means blocked
535 PTRACE(3, "STUN\tNo response to server " << serverAddress
<< ':' << serverPort
<< " - " << socket
.GetErrorText(PChannel::LastReadError
));
536 return natType
= BlockedNat
; // No response usually means blocked
539 PSTUNMappedAddress
* mappedAddress
= (PSTUNMappedAddress
*)responseI
.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
540 if (mappedAddress
== NULL
) {
541 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
542 return natType
= UnknownNat
; // Protocol error
545 PIPSocket::Address mappedAddressI
= mappedAddress
->GetIP();
546 WORD mappedPortI
= mappedAddress
->port
;
547 bool notNAT
= socket
.GetPort() == mappedPortI
&& PIPSocket::IsLocalHost(mappedAddressI
);
549 /* Test II - the client sends a Binding Request with both the "change IP"
550 and "change port" flags from the CHANGE-REQUEST attribute set. */
551 PSTUNMessage
requestII(PSTUNMessage::BindingRequest
);
552 requestII
.AddAttribute(PSTUNChangeRequest(true, true));
553 PSTUNMessage responseII
;
554 bool testII
= responseII
.Poll(socket
, requestII
, pollRetries
);
557 // Is not NAT or symmetric firewall
558 return natType
= (testII
? OpenNat
: SymmetricFirewall
);
562 return natType
= ConeNat
;
564 PSTUNChangedAddress
* changedAddress
= (PSTUNChangedAddress
*)responseI
.FindAttribute(PSTUNAttribute::CHANGED_ADDRESS
);
565 if (changedAddress
== NULL
)
566 return natType
= UnknownNat
; // Protocol error
568 // Send test I to another server, to see if restricted or symmetric
569 PIPSocket::Address secondaryServer
= changedAddress
->GetIP();
570 WORD secondaryPort
= changedAddress
->port
;
571 socket
.SetSendAddress(secondaryServer
, secondaryPort
);
572 PSTUNMessage
requestI2(PSTUNMessage::BindingRequest
);
573 requestI2
.AddAttribute(PSTUNChangeRequest(false, false));
574 PSTUNMessage responseI2
;
575 if (!responseI2
.Poll(socket
, requestI2
, pollRetries
)) {
576 PTRACE(2, "STUN\tPoll of secondary server " << secondaryServer
<< ':' << secondaryPort
577 << " failed, NAT partially blocked by firwall rules.");
578 return natType
= PartialBlockedNat
;
581 mappedAddress
= (PSTUNMappedAddress
*)responseI2
.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
582 if (mappedAddress
== NULL
) {
583 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
584 return UnknownNat
; // Protocol error
587 if (mappedAddress
->port
!= mappedPortI
|| mappedAddress
->GetIP() != mappedAddressI
)
588 return natType
= SymmetricNat
;
590 socket
.SetSendAddress(serverAddress
, serverPort
);
591 PSTUNMessage
requestIII(PSTUNMessage::BindingRequest
);
592 requestIII
.SetAttribute(PSTUNChangeRequest(false, true));
593 PSTUNMessage responseIII
;
594 return natType
= (responseIII
.Poll(socket
, requestIII
, pollRetries
) ? RestrictedNat
: PortRestrictedNat
);
598 PString
PSTUNClient::GetNatTypeString(NatTypes type
)
600 static const char * const Names
[NumNatTypes
] = {
605 "Port Restricted NAT",
607 "Symmetric Firewall",
612 if (type
< NumNatTypes
)
615 return psprintf("<NATType %u>", type
);
619 PSTUNClient::RTPSupportTypes
PSTUNClient::IsSupportingRTP(BOOL force
)
621 switch (GetNatType(force
)) {
623 // types that do support RTP
628 // types that support RTP if media sent first
629 case SymmetricFirewall
:
631 case PortRestrictedNat
:
632 return RTPIfSendMedia
;
634 // types that do not support RTP
637 return RTPUnsupported
;
639 // types that have unknown RTP support
641 case PartialBlockedNat
:
649 BOOL
PSTUNClient::GetExternalAddress(PIPSocket::Address
& externalAddress
,
650 const PTimeInterval
& maxAge
)
652 if (cachedExternalAddress
.IsValid() && (PTime() - timeAddressObtained
< maxAge
)) {
653 externalAddress
= cachedExternalAddress
;
657 externalAddress
= 0; // Set to invalid address
660 if (!OpenSocket(socket
, singlePortInfo
, PIPSocket::GetDefaultIpAny()))
663 PSTUNMessage
request(PSTUNMessage::BindingRequest
);
664 request
.AddAttribute(PSTUNChangeRequest(false, false));
665 PSTUNMessage response
;
666 if (!response
.Poll(socket
, request
, pollRetries
))
668 PTRACE(1, "STUN\tServer " << serverAddress
<< ':' << serverPort
<< " unexpectedly went offline.");
672 PSTUNMappedAddress
* mappedAddress
= (PSTUNMappedAddress
*)response
.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
673 if (mappedAddress
== NULL
)
675 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
680 externalAddress
= cachedExternalAddress
= mappedAddress
->GetIP();
681 timeAddressObtained
= PTime();
686 BOOL
PSTUNClient::CreateSocket(PUDPSocket
* & socket
, const PIPSocket::Address
& binding
, WORD localPort
)
690 switch (GetNatType(FALSE
)) {
693 case PortRestrictedNat
:
697 if (singlePortInfo
.basePort
== 0 || singlePortInfo
.basePort
> singlePortInfo
.maxPort
)
699 PTRACE(1, "STUN\tInvalid local UDP port range "
700 << singlePortInfo
.currentPort
<< '-' << singlePortInfo
.maxPort
);
705 default : // UnknownNet, SymmetricFirewall, BlockedNat
706 PTRACE(1, "STUN\tCannot create socket using NAT type " << GetNatTypeName());
710 PSTUNUDPSocket
* stunSocket
= new PSTUNUDPSocket
;
714 opened
= OpenSocket(*stunSocket
, singlePortInfo
, binding
);
716 PortInfo portInfo
= localPort
;
717 opened
= OpenSocket(*stunSocket
, portInfo
, binding
);
722 PSTUNMessage
request(PSTUNMessage::BindingRequest
);
723 request
.AddAttribute(PSTUNChangeRequest(false, false));
724 PSTUNMessage response
;
726 if (response
.Poll(*stunSocket
, request
, pollRetries
))
728 PSTUNMappedAddress
* mappedAddress
= (PSTUNMappedAddress
*)response
.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
729 if (mappedAddress
!= NULL
)
731 stunSocket
->externalIP
= mappedAddress
->GetIP();
732 if (GetNatType(FALSE
) != SymmetricNat
)
733 stunSocket
->port
= mappedAddress
->port
;
734 stunSocket
->SetSendAddress(0, 0);
735 stunSocket
->SetReadTimeout(PMaxTimeInterval
);
740 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
743 PTRACE(1, "STUN\tServer " << serverAddress
<< ':' << serverPort
<< " unexpectedly went offline.");
751 BOOL
PSTUNClient::CreateSocketPair(PUDPSocket
* & socket1
,
752 PUDPSocket
* & socket2
,
753 const PIPSocket::Address
& binding
)
758 switch (GetNatType(FALSE
)) {
761 case PortRestrictedNat
:
765 if (pairedPortInfo
.basePort
== 0 || pairedPortInfo
.basePort
> pairedPortInfo
.maxPort
)
767 PTRACE(1, "STUN\tInvalid local UDP port range "
768 << pairedPortInfo
.currentPort
<< '-' << pairedPortInfo
.maxPort
);
773 default : // UnknownNet, SymmetricFirewall, BlockedNat
774 PTRACE(1, "STUN\tCannot create socket pair using NAT type " << GetNatTypeName());
780 PList
<PSTUNUDPSocket
> stunSocket
;
781 PList
<PSTUNMessage
> request
;
782 PList
<PSTUNMessage
> response
;
784 for (i
= 0; i
< numSocketsForPairing
; i
++)
786 PINDEX idx
= stunSocket
.Append(new PSTUNUDPSocket
);
787 if (!OpenSocket(stunSocket
[idx
], pairedPortInfo
, binding
)) {
788 PTRACE(1, "STUN\tUnable to open socket to server " << serverAddress
);
792 idx
= request
.Append(new PSTUNMessage(PSTUNMessage::BindingRequest
));
793 request
[idx
].AddAttribute(PSTUNChangeRequest(false, false));
795 response
.Append(new PSTUNMessage
);
798 for (i
= 0; i
< numSocketsForPairing
; i
++)
800 if (!response
[i
].Poll(stunSocket
[i
], request
[i
], pollRetries
))
802 PTRACE(1, "STUN\tServer " << serverAddress
<< ':' << serverPort
<< " unexpectedly went offline.");
807 for (i
= 0; i
< numSocketsForPairing
; i
++)
809 PSTUNMappedAddress
* mappedAddress
= (PSTUNMappedAddress
*)response
[i
].FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
810 if (mappedAddress
== NULL
)
812 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
815 if (GetNatType(FALSE
) != SymmetricNat
)
816 stunSocket
[i
].port
= mappedAddress
->port
;
817 stunSocket
[i
].externalIP
= mappedAddress
->GetIP();
820 for (i
= 0; i
< numSocketsForPairing
; i
++)
822 for (PINDEX j
= 0; j
< numSocketsForPairing
; j
++)
824 if ((stunSocket
[i
].port
&1) == 0 && (stunSocket
[i
].port
+1) == stunSocket
[j
].port
) {
825 stunSocket
[i
].SetSendAddress(0, 0);
826 stunSocket
[i
].SetReadTimeout(PMaxTimeInterval
);
827 stunSocket
[j
].SetSendAddress(0, 0);
828 stunSocket
[j
].SetReadTimeout(PMaxTimeInterval
);
829 socket1
= &stunSocket
[i
];
830 socket2
= &stunSocket
[j
];
831 stunSocket
.DisallowDeleteObjects();
832 stunSocket
.Remove(socket1
);
833 stunSocket
.Remove(socket2
);
834 stunSocket
.AllowDeleteObjects();
840 PTRACE(2, "STUN\tCould not get a pair of adjacent port numbers from NAT");
844 BOOL
PSTUNClient::IsAvailable()
847 switch (GetNatType(FALSE
)) {
850 case PortRestrictedNat
:
854 if (pairedPortInfo
.basePort
== 0 || pairedPortInfo
.basePort
> pairedPortInfo
.maxPort
)
859 default : // UnknownNet, SymmetricFirewall, BlockedNat
866 ////////////////////////////////////////////////////////////////
868 PSTUNUDPSocket::PSTUNUDPSocket()
874 BOOL
PSTUNUDPSocket::GetLocalAddress(Address
& addr
)
876 if (!externalIP
.IsValid())
877 return PUDPSocket::GetLocalAddress(addr
);
884 BOOL
PSTUNUDPSocket::GetLocalAddress(Address
& addr
, WORD
& port
)
886 if (!externalIP
.IsValid())
887 return PUDPSocket::GetLocalAddress(addr
, port
);
895 // End of File ////////////////////////////////////////////////////////////////