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.16 2004/11/25 07:23:46 csoutheren
28 * Added IsSupportingRTP function to simplify detecting when STUN supports RTP
30 * Revision 1.15 2004/10/26 05:58:23 csoutheren
31 * Increased timeout on STUN responses to avoid spurious STUN failures due
32 * to network trsffic/congestion etc
34 * Revision 1.14 2004/08/18 13:16:07 rjongbloed
35 * Fixed STUN CreateSocketPair so first socket is always even.
37 * Revision 1.13 2004/03/14 05:47:52 rjongbloed
38 * Fixed incorrect detection of symmetric NAT (eg Linux masquerading) and also
39 * some NAT systems which are partially blocked due to firewall rules.
41 * Revision 1.12 2004/02/24 11:15:48 rjongbloed
42 * Added function to get external router address, also did a bunch of documentation.
44 * Revision 1.11 2004/02/17 11:11:05 rjongbloed
45 * Added missing #pragma pack() to turn off byte alignment for the last class, thanks Ted Szoczei
47 * Revision 1.10 2004/01/17 17:54:02 rjongbloed
48 * Added function to get server name from STUN client.
50 * Revision 1.9 2003/10/08 22:00:18 dereksmithies
51 * Fix unsigned/signed warning message. Thanks to Craig Southeren.
53 * Revision 1.8 2003/10/05 00:56:25 rjongbloed
54 * Rewrite of STUN to not to use imported code with undesirable license.
56 * Revision 1.5 2003/02/05 06:26:49 robertj
57 * More work in making the STUN usable for Symmetric NAT systems.
59 * Revision 1.4 2003/02/04 07:02:17 robertj
60 * Added ip/port version of constructor.
61 * Removed creating sockets for Open type.
63 * Revision 1.3 2003/02/04 05:55:04 craigs
64 * Added socket pair function
66 * Revision 1.2 2003/02/04 05:06:24 craigs
69 * Revision 1.1 2003/02/04 03:31:04 robertj
75 #pragma implementation "pstun.h"
80 #include <ptclib/pstun.h>
81 #include <ptclib/random.h>
84 // Sample server is at larry.gloo.net
87 ///////////////////////////////////////////////////////////////////////
89 PSTUNClient::PSTUNClient(const PString
& server
,
90 WORD portBase
, WORD portMax
,
91 WORD portPairBase
, WORD portPairMax
)
93 cachedExternalAddress(0),
94 timeAddressObtained(0)
96 serverPort
= DefaultPort
;
100 SetPortRanges(portBase
, portMax
, portPairBase
, portPairMax
);
104 PSTUNClient::PSTUNClient(const PIPSocket::Address
& address
, WORD port
,
105 WORD portBase
, WORD portMax
,
106 WORD portPairBase
, WORD portPairMax
)
107 : serverAddress(address
),
109 cachedExternalAddress(0),
110 timeAddressObtained(0)
113 SetPortRanges(portBase
, portMax
, portPairBase
, portPairMax
);
117 void PSTUNClient::Construct()
119 singlePortInfo
.basePort
= 0;
120 singlePortInfo
.maxPort
= 0;
121 singlePortInfo
.currentPort
= 0;
122 pairedPortInfo
.basePort
= 0;
123 pairedPortInfo
.maxPort
= 0;
124 pairedPortInfo
.currentPort
= 0;
125 numSocketsForPairing
= 4;
126 natType
= UnknownNat
;
130 PString
PSTUNClient::GetServer() const
133 str
<< serverAddress
<< ':' << serverPort
;
138 BOOL
PSTUNClient::SetServer(const PString
& server
)
140 PINDEX colon
= server
.Find(':');
141 if (colon
== P_MAX_INDEX
) {
142 if (!PIPSocket::GetHostAddress(server
, serverAddress
))
146 if (!PIPSocket::GetHostAddress(server
.Left(colon
), serverAddress
))
148 serverPort
= PIPSocket::GetPortByService("udp", server
.Mid(colon
+1));
151 return serverAddress
.IsValid() && serverPort
!= 0;
155 BOOL
PSTUNClient::SetServer(const PIPSocket::Address
& address
, WORD port
)
157 serverAddress
= address
;
159 return serverAddress
.IsValid() && serverPort
!= 0;
163 void PSTUNClient::SetPortRanges(WORD portBase
, WORD portMax
,
164 WORD portPairBase
, WORD portPairMax
)
166 singlePortInfo
.mutex
.Wait();
168 singlePortInfo
.basePort
= portBase
;
170 singlePortInfo
.maxPort
= 0;
171 else if (portMax
== 0)
172 singlePortInfo
.maxPort
= (WORD
)(singlePortInfo
.basePort
+99);
173 else if (portMax
< portBase
)
174 singlePortInfo
.maxPort
= portBase
;
176 singlePortInfo
.maxPort
= portMax
;
178 singlePortInfo
.currentPort
= singlePortInfo
.basePort
;
180 singlePortInfo
.mutex
.Signal();
182 pairedPortInfo
.mutex
.Wait();
184 pairedPortInfo
.basePort
= (WORD
)((portPairBase
+1)&0xfffe);
185 if (portPairBase
== 0) {
186 pairedPortInfo
.basePort
= 0;
187 pairedPortInfo
.maxPort
= 0;
189 else if (portPairMax
== 0)
190 pairedPortInfo
.maxPort
= (WORD
)(pairedPortInfo
.basePort
+99);
191 else if (portPairMax
< portPairBase
)
192 pairedPortInfo
.maxPort
= portPairBase
;
194 pairedPortInfo
.maxPort
= portPairMax
;
196 pairedPortInfo
.currentPort
= pairedPortInfo
.basePort
;
198 pairedPortInfo
.mutex
.Signal();
204 struct PSTUNAttribute
207 MAPPED_ADDRESS
= 0x0001,
208 RESPONSE_ADDRESS
= 0x0002,
209 CHANGE_REQUEST
= 0x0003,
210 SOURCE_ADDRESS
= 0x0004,
211 CHANGED_ADDRESS
= 0x0005,
214 MESSAGE_INTEGRITY
= 0x0008,
216 UNKNOWN_ATTRIBUTES
= 0x000a,
217 REFLECTED_FROM
= 0x000b,
223 PSTUNAttribute
* GetNext() const { return (PSTUNAttribute
*)(((const BYTE
*)this)+length
+4); }
226 class PSTUNAddressAttribute
: public PSTUNAttribute
234 PIPSocket::Address
GetIP() const { return PIPSocket::Address(4, ip
); }
237 enum { SizeofAddressAttribute
= sizeof(BYTE
)+sizeof(BYTE
)+sizeof(WORD
)+sizeof(PIPSocket::Address
) };
238 void InitAddrAttr(Types newType
)
240 type
= (WORD
)newType
;
241 length
= SizeofAddressAttribute
;
245 bool IsValidAddrAttr(Types checkType
) const
247 return type
== checkType
&& length
== SizeofAddressAttribute
;
251 class PSTUNMappedAddress
: public PSTUNAddressAttribute
254 void Initialise() { InitAddrAttr(MAPPED_ADDRESS
); }
255 bool IsValid() const { return IsValidAddrAttr(MAPPED_ADDRESS
); }
258 class PSTUNChangedAddress
: public PSTUNAddressAttribute
261 void Initialise() { InitAddrAttr(CHANGED_ADDRESS
); }
262 bool IsValid() const { return IsValidAddrAttr(CHANGED_ADDRESS
); }
265 class PSTUNChangeRequest
: public PSTUNAttribute
270 PSTUNChangeRequest() { }
272 PSTUNChangeRequest(bool changeIP
, bool changePort
)
275 SetChangeIP(changeIP
);
276 SetChangePort(changePort
);
281 type
= CHANGE_REQUEST
;
282 length
= sizeof(flags
);
283 memset(flags
, 0, sizeof(flags
));
285 bool IsValid() const { return type
== CHANGE_REQUEST
&& length
== sizeof(flags
); }
287 bool GetChangeIP() const { return (flags
[3]&4) != 0; }
288 void SetChangeIP(bool on
) { if (on
) flags
[3] |= 4; else flags
[3] &= ~4; }
290 bool GetChangePort() const { return (flags
[3]&2) != 0; }
291 void SetChangePort(bool on
) { if (on
) flags
[3] |= 2; else flags
[3] &= ~2; }
294 class PSTUNMessageIntegrity
: public PSTUNAttribute
301 type
= MESSAGE_INTEGRITY
;
302 length
= sizeof(hmac
);
303 memset(hmac
, 0, sizeof(hmac
));
305 bool IsValid() const { return type
== MESSAGE_INTEGRITY
&& length
== sizeof(hmac
); }
308 struct PSTUNMessageHeader
312 BYTE transactionId
[16];
319 class PSTUNMessage
: public PBYTEArray
323 BindingRequest
= 0x0001,
324 BindingResponse
= 0x0101,
325 BindingError
= 0x0111,
327 SharedSecretRequest
= 0x0002,
328 SharedSecretResponse
= 0x0102,
329 SharedSecretError
= 0x0112,
335 PSTUNMessage(MsgType newType
, const BYTE
* id
= NULL
)
336 : PBYTEArray(sizeof(PSTUNMessageHeader
))
338 SetType(newType
, id
);
341 void SetType(MsgType newType
, const BYTE
* id
= NULL
)
343 SetMinSize(sizeof(PSTUNMessageHeader
));
344 PSTUNMessageHeader
* hdr
= (PSTUNMessageHeader
*)theArray
;
345 hdr
->msgType
= (WORD
)newType
;
346 for (PINDEX i
= 0; i
< ((PINDEX
)sizeof(hdr
->transactionId
)); i
++)
347 hdr
->transactionId
[i
] = id
!= NULL
? id
[i
] : (BYTE
)PRandom::Number();
350 const PSTUNMessageHeader
* operator->() const { return (PSTUNMessageHeader
*)theArray
; }
352 PSTUNAttribute
* GetFirstAttribute() { return (PSTUNAttribute
*)(theArray
+sizeof(PSTUNMessageHeader
)); }
356 int length
= ((PSTUNMessageHeader
*)theArray
)->msgLength
;
357 PSTUNAttribute
* attrib
= GetFirstAttribute();
359 length
-= attrib
->length
+ 4;
360 attrib
= attrib
->GetNext();
363 return length
== 0; // Exactly correct length
366 void AddAttribute(const PSTUNAttribute
& attribute
)
368 PSTUNMessageHeader
* hdr
= (PSTUNMessageHeader
*)theArray
;
369 int oldLength
= hdr
->msgLength
;
370 int attrSize
= attribute
.length
+ 4;
371 int newLength
= oldLength
+ attrSize
;
372 hdr
->msgLength
= (WORD
)newLength
;
373 // hdr pointer may be invalidated by next statement
374 SetMinSize(newLength
+sizeof(PSTUNMessageHeader
));
375 memcpy(theArray
+sizeof(PSTUNMessageHeader
)+oldLength
, &attribute
, attrSize
);
378 void SetAttribute(const PSTUNAttribute
& attribute
)
380 int length
= ((PSTUNMessageHeader
*)theArray
)->msgLength
;
381 PSTUNAttribute
* attrib
= GetFirstAttribute();
383 if (attrib
->type
== attribute
.type
) {
384 if (attrib
->length
== attribute
.length
)
392 length
-= attrib
->length
+ 4;
393 attrib
= attrib
->GetNext();
396 AddAttribute(attribute
);
399 PSTUNAttribute
* FindAttribute(PSTUNAttribute::Types type
)
401 int length
= ((PSTUNMessageHeader
*)theArray
)->msgLength
;
402 PSTUNAttribute
* attrib
= GetFirstAttribute();
404 if (attrib
->type
== type
)
407 length
-= attrib
->length
+ 4;
408 attrib
= attrib
->GetNext();
414 bool Read(PUDPSocket
& socket
)
416 if (!socket
.Read(GetPointer(1000), 1000))
418 SetSize(socket
.GetLastReadCount());
422 bool Write(PUDPSocket
& socket
) const
424 return socket
.Write(theArray
, ((PSTUNMessageHeader
*)theArray
)->msgLength
+sizeof(PSTUNMessageHeader
)) != FALSE
;
427 bool Poll(PUDPSocket
& socket
, const PSTUNMessage
& request
)
429 for (int retry
= 0; retry
< 3; retry
++) {
430 if (!request
.Write(socket
))
433 if (Read(socket
) && Validate() &&
434 memcmp(request
->transactionId
, (*this)->transactionId
, sizeof(request
->transactionId
)) == 0)
443 bool PSTUNClient::OpenSocket(PUDPSocket
& socket
, PortInfo
& portInfo
) const
445 PWaitAndSignal
mutex(portInfo
.mutex
);
447 WORD startPort
= portInfo
.currentPort
;
450 portInfo
.currentPort
++;
451 if (portInfo
.currentPort
> portInfo
.maxPort
)
452 portInfo
.currentPort
= portInfo
.basePort
;
454 if (socket
.Listen(1, portInfo
.currentPort
)) {
455 socket
.SetSendAddress(serverAddress
, serverPort
);
456 socket
.SetReadTimeout(5000);
460 } while (portInfo
.currentPort
!= startPort
);
462 PTRACE(1, "STUN\tFailed to bind to local UDP port in range "
463 << portInfo
.currentPort
<< '-' << portInfo
.maxPort
);
468 PSTUNClient::NatTypes
PSTUNClient::GetNatType(BOOL force
)
470 if (!force
&& natType
!= UnknownNat
)
474 if (!OpenSocket(socket
, singlePortInfo
))
475 return natType
= UnknownNat
;
479 /* test I - the client sends a STUN Binding Request to a server, without
480 any flags set in the CHANGE-REQUEST attribute, and without the
481 RESPONSE-ADDRESS attribute. This causes the server to send the response
482 back to the address and port that the request came from. */
483 PSTUNMessage
requestI(PSTUNMessage::BindingRequest
);
484 requestI
.AddAttribute(PSTUNChangeRequest(false, false));
485 PSTUNMessage responseI
;
486 if (!responseI
.Poll(socket
, requestI
)) {
487 if (socket
.GetErrorCode(PChannel::LastWriteError
) != PChannel::NoError
) {
488 PTRACE(1, "STUN\tError writing to server " << serverAddress
<< ':' << serverPort
<< " - " << socket
.GetErrorText(PChannel::LastWriteError
));
489 return natType
= UnknownNat
; // No response usually means blocked
492 PTRACE(3, "STUN\tNo response to server " << serverAddress
<< ':' << serverPort
<< " - " << socket
.GetErrorText(PChannel::LastReadError
));
493 return natType
= BlockedNat
; // No response usually means blocked
496 PSTUNMappedAddress
* mappedAddress
= (PSTUNMappedAddress
*)responseI
.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
497 if (mappedAddress
== NULL
) {
498 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
499 return natType
= UnknownNat
; // Protocol error
502 PIPSocket::Address mappedAddressI
= mappedAddress
->GetIP();
503 WORD mappedPortI
= mappedAddress
->port
;
504 bool notNAT
= socket
.GetPort() == mappedPortI
&& PIPSocket::IsLocalHost(mappedAddressI
);
506 /* Test II - the client sends a Binding Request with both the "change IP"
507 and "change port" flags from the CHANGE-REQUEST attribute set. */
508 PSTUNMessage
requestII(PSTUNMessage::BindingRequest
);
509 requestII
.AddAttribute(PSTUNChangeRequest(true, true));
510 PSTUNMessage responseII
;
511 bool testII
= responseII
.Poll(socket
, requestII
);
514 // Is not NAT or symmetric firewall
515 return natType
= (testII
? OpenNat
: SymmetricFirewall
);
519 return natType
= ConeNat
;
521 PSTUNChangedAddress
* changedAddress
= (PSTUNChangedAddress
*)responseI
.FindAttribute(PSTUNAttribute::CHANGED_ADDRESS
);
522 if (changedAddress
== NULL
)
523 return natType
= UnknownNat
; // Protocol error
525 // Send test I to another server, to see if restricted or symmetric
526 PIPSocket::Address secondaryServer
= changedAddress
->GetIP();
527 WORD secondaryPort
= changedAddress
->port
;
528 socket
.SetSendAddress(secondaryServer
, secondaryPort
);
529 PSTUNMessage
requestI2(PSTUNMessage::BindingRequest
);
530 requestI2
.AddAttribute(PSTUNChangeRequest(false, false));
531 PSTUNMessage responseI2
;
532 if (!responseI2
.Poll(socket
, requestI2
)) {
533 PTRACE(2, "STUN\tPoll of secondary server " << secondaryServer
<< ':' << secondaryPort
534 << " failed, NAT partially blocked by firwall rules.");
535 return natType
= PartialBlockedNat
;
538 mappedAddress
= (PSTUNMappedAddress
*)responseI2
.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
539 if (mappedAddress
== NULL
) {
540 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
541 return UnknownNat
; // Protocol error
544 if (mappedAddress
->port
!= mappedPortI
|| mappedAddress
->GetIP() != mappedAddressI
)
545 return natType
= SymmetricNat
;
547 socket
.SetSendAddress(serverAddress
, serverPort
);
548 PSTUNMessage
requestIII(PSTUNMessage::BindingRequest
);
549 requestIII
.SetAttribute(PSTUNChangeRequest(false, true));
550 PSTUNMessage responseIII
;
551 return natType
= (responseIII
.Poll(socket
, requestIII
) ? RestrictedNat
: PortRestrictedNat
);
555 PString
PSTUNClient::GetNatTypeName(BOOL force
)
557 static const char * const Names
[NumNatTypes
] = {
562 "Port Restricted NAT",
564 "Symmetric Firewall",
569 return Names
[GetNatType(force
)];
573 PSTUNClient::RTPSupportTypes
PSTUNClient::IsSupportingRTP(BOOL force
)
575 switch (GetNatType(force
)) {
577 // types that do support RTP
582 // types that support RTP if media sent first
583 case SymmetricFirewall
:
585 case PortRestrictedNat
:
586 return RTPIfSendMedia
;
588 // types that do not support RTP
591 return RTPUnsupported
;
593 // types that have unknown RTP support
595 case PartialBlockedNat
:
603 BOOL
PSTUNClient::GetExternalAddress(PIPSocket::Address
& externalAddress
,
604 const PTimeInterval
& maxAge
)
606 if (cachedExternalAddress
.IsValid() && (PTime() - timeAddressObtained
> maxAge
)) {
607 externalAddress
= cachedExternalAddress
;
611 externalAddress
= 0; // Set to invalid address
614 if (!OpenSocket(socket
, singlePortInfo
))
617 PSTUNMessage
request(PSTUNMessage::BindingRequest
);
618 request
.AddAttribute(PSTUNChangeRequest(false, false));
619 PSTUNMessage response
;
620 if (!response
.Poll(socket
, request
))
622 PTRACE(1, "STUN\tServer " << serverAddress
<< ':' << serverPort
<< " unexpectedly went offline.");
626 PSTUNMappedAddress
* mappedAddress
= (PSTUNMappedAddress
*)response
.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
627 if (mappedAddress
== NULL
)
629 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
634 externalAddress
= cachedExternalAddress
= mappedAddress
->GetIP();
635 timeAddressObtained
= PTime();
640 BOOL
PSTUNClient::CreateSocket(PUDPSocket
* & socket
)
644 switch (GetNatType(FALSE
)) {
647 case PortRestrictedNat
:
651 if (singlePortInfo
.basePort
== 0 || singlePortInfo
.basePort
> singlePortInfo
.maxPort
)
653 PTRACE(1, "STUN\tInvalid local UDP port range "
654 << singlePortInfo
.currentPort
<< '-' << singlePortInfo
.maxPort
);
659 default : // UnknownNet, SymmetricFirewall, BlockedNat
660 PTRACE(1, "STUN\tCannot create socket using NAT type " << GetNatTypeName());
664 PSTUNUDPSocket
* stunSocket
= new PSTUNUDPSocket
;
665 if (OpenSocket(*stunSocket
, singlePortInfo
))
667 PSTUNMessage
request(PSTUNMessage::BindingRequest
);
668 request
.AddAttribute(PSTUNChangeRequest(false, false));
669 PSTUNMessage response
;
671 if (response
.Poll(*stunSocket
, request
))
673 PSTUNMappedAddress
* mappedAddress
= (PSTUNMappedAddress
*)response
.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
674 if (mappedAddress
!= NULL
)
676 stunSocket
->externalIP
= mappedAddress
->GetIP();
677 if (GetNatType(FALSE
) != SymmetricNat
)
678 stunSocket
->port
= mappedAddress
->port
;
679 stunSocket
->SetSendAddress(0, 0);
680 stunSocket
->SetReadTimeout(PMaxTimeInterval
);
685 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
688 PTRACE(1, "STUN\tServer " << serverAddress
<< ':' << serverPort
<< " unexpectedly went offline.");
696 BOOL
PSTUNClient::CreateSocketPair(PUDPSocket
* & socket1
,
697 PUDPSocket
* & socket2
)
702 switch (GetNatType(FALSE
)) {
705 case PortRestrictedNat
:
709 if (pairedPortInfo
.basePort
== 0 || pairedPortInfo
.basePort
> pairedPortInfo
.maxPort
)
711 PTRACE(1, "STUN\tInvalid local UDP port range "
712 << pairedPortInfo
.currentPort
<< '-' << pairedPortInfo
.maxPort
);
717 default : // UnknownNet, SymmetricFirewall, BlockedNat
718 PTRACE(1, "STUN\tCannot create socket pair using NAT type " << GetNatTypeName());
724 PList
<PSTUNUDPSocket
> stunSocket
;
725 PList
<PSTUNMessage
> request
;
726 PList
<PSTUNMessage
> response
;
728 for (i
= 0; i
< numSocketsForPairing
; i
++)
730 PINDEX idx
= stunSocket
.Append(new PSTUNUDPSocket
);
731 if (!OpenSocket(stunSocket
[idx
], pairedPortInfo
))
734 idx
= request
.Append(new PSTUNMessage(PSTUNMessage::BindingRequest
));
735 request
[idx
].AddAttribute(PSTUNChangeRequest(false, false));
737 response
.Append(new PSTUNMessage
);
740 for (i
= 0; i
< numSocketsForPairing
; i
++)
742 if (!response
[i
].Poll(stunSocket
[i
], request
[i
]))
744 PTRACE(1, "STUN\tServer " << serverAddress
<< ':' << serverPort
<< " unexpectedly went offline.");
749 for (i
= 0; i
< numSocketsForPairing
; i
++)
751 PSTUNMappedAddress
* mappedAddress
= (PSTUNMappedAddress
*)response
[i
].FindAttribute(PSTUNAttribute::MAPPED_ADDRESS
);
752 if (mappedAddress
== NULL
)
754 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress
<< ':' << serverPort
);
757 if (GetNatType(FALSE
) != SymmetricNat
)
758 stunSocket
[i
].port
= mappedAddress
->port
;
759 stunSocket
[i
].externalIP
= mappedAddress
->GetIP();
762 for (i
= 0; i
< numSocketsForPairing
; i
++)
764 for (int j
= 0; j
< numSocketsForPairing
; j
++)
766 if ((stunSocket
[i
].port
&1) == 0 && (stunSocket
[i
].port
+1) == stunSocket
[j
].port
)
768 stunSocket
[i
].SetSendAddress(0, 0);
769 stunSocket
[i
].SetReadTimeout(PMaxTimeInterval
);
770 stunSocket
[j
].SetSendAddress(0, 0);
771 stunSocket
[j
].SetReadTimeout(PMaxTimeInterval
);
772 socket1
= &stunSocket
[i
];
773 socket2
= &stunSocket
[j
];
774 stunSocket
.DisallowDeleteObjects();
775 stunSocket
.Remove(socket1
);
776 stunSocket
.Remove(socket2
);
777 stunSocket
.AllowDeleteObjects();
783 PTRACE(2, "STUN\tCould not get a pair of adjacent port numbers from NAT");
788 ////////////////////////////////////////////////////////////////
790 PSTUNUDPSocket::PSTUNUDPSocket()
796 BOOL
PSTUNUDPSocket::GetLocalAddress(Address
& addr
)
798 if (!externalIP
.IsValid())
799 return PUDPSocket::GetLocalAddress(addr
);
806 BOOL
PSTUNUDPSocket::GetLocalAddress(Address
& addr
, WORD
& port
)
808 if (!externalIP
.IsValid())
809 return PUDPSocket::GetLocalAddress(addr
, port
);
817 // End of File ////////////////////////////////////////////////////////////////