Added IsSupportingRTP function to simplify detecting when STUN supports RTP
[pwlib.git] / src / ptclib / pstun.cxx
blob9f13bd6616e4b6149e325d32aa6679ce25fa01c3
1 /*
2 * pstun.cxx
4 * STUN Client
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
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
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
67 * Added new functions
69 * Revision 1.1 2003/02/04 03:31:04 robertj
70 * Added STUN
74 #ifdef __GNUC__
75 #pragma implementation "pstun.h"
76 #endif
79 #include <ptlib.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)
92 : serverAddress(0),
93 cachedExternalAddress(0),
94 timeAddressObtained(0)
96 serverPort = DefaultPort;
97 Construct();
99 SetServer(server);
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),
108 serverPort(port),
109 cachedExternalAddress(0),
110 timeAddressObtained(0)
112 Construct();
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
132 PStringStream str;
133 str << serverAddress << ':' << serverPort;
134 return str;
138 BOOL PSTUNClient::SetServer(const PString & server)
140 PINDEX colon = server.Find(':');
141 if (colon == P_MAX_INDEX) {
142 if (!PIPSocket::GetHostAddress(server, serverAddress))
143 return FALSE;
145 else {
146 if (!PIPSocket::GetHostAddress(server.Left(colon), serverAddress))
147 return FALSE;
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;
158 serverPort = port;
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;
169 if (portBase == 0)
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;
175 else
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;
193 else
194 pairedPortInfo.maxPort = portPairMax;
196 pairedPortInfo.currentPort = pairedPortInfo.basePort;
198 pairedPortInfo.mutex.Signal();
202 #pragma pack(1)
204 struct PSTUNAttribute
206 enum Types {
207 MAPPED_ADDRESS = 0x0001,
208 RESPONSE_ADDRESS = 0x0002,
209 CHANGE_REQUEST = 0x0003,
210 SOURCE_ADDRESS = 0x0004,
211 CHANGED_ADDRESS = 0x0005,
212 USERNAME = 0x0006,
213 PASSWORD = 0x0007,
214 MESSAGE_INTEGRITY = 0x0008,
215 ERROR_CODE = 0x0009,
216 UNKNOWN_ATTRIBUTES = 0x000a,
217 REFLECTED_FROM = 0x000b,
220 PUInt16b type;
221 PUInt16b length;
223 PSTUNAttribute * GetNext() const { return (PSTUNAttribute *)(((const BYTE *)this)+length+4); }
226 class PSTUNAddressAttribute : public PSTUNAttribute
228 public:
229 BYTE pad;
230 BYTE family;
231 PUInt16b port;
232 BYTE ip[4];
234 PIPSocket::Address GetIP() const { return PIPSocket::Address(4, ip); }
236 protected:
237 enum { SizeofAddressAttribute = sizeof(BYTE)+sizeof(BYTE)+sizeof(WORD)+sizeof(PIPSocket::Address) };
238 void InitAddrAttr(Types newType)
240 type = (WORD)newType;
241 length = SizeofAddressAttribute;
242 pad = 0;
243 family = 1;
245 bool IsValidAddrAttr(Types checkType) const
247 return type == checkType && length == SizeofAddressAttribute;
251 class PSTUNMappedAddress : public PSTUNAddressAttribute
253 public:
254 void Initialise() { InitAddrAttr(MAPPED_ADDRESS); }
255 bool IsValid() const { return IsValidAddrAttr(MAPPED_ADDRESS); }
258 class PSTUNChangedAddress : public PSTUNAddressAttribute
260 public:
261 void Initialise() { InitAddrAttr(CHANGED_ADDRESS); }
262 bool IsValid() const { return IsValidAddrAttr(CHANGED_ADDRESS); }
265 class PSTUNChangeRequest : public PSTUNAttribute
267 public:
268 BYTE flags[4];
270 PSTUNChangeRequest() { }
272 PSTUNChangeRequest(bool changeIP, bool changePort)
274 Initialise();
275 SetChangeIP(changeIP);
276 SetChangePort(changePort);
279 void Initialise()
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
296 public:
297 BYTE hmac[20];
299 void Initialise()
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
310 PUInt16b msgType;
311 PUInt16b msgLength;
312 BYTE transactionId[16];
316 #pragma pack()
319 class PSTUNMessage : public PBYTEArray
321 public:
322 enum MsgType {
323 BindingRequest = 0x0001,
324 BindingResponse = 0x0101,
325 BindingError = 0x0111,
327 SharedSecretRequest = 0x0002,
328 SharedSecretResponse = 0x0102,
329 SharedSecretError = 0x0112,
332 PSTUNMessage()
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)); }
354 bool Validate()
356 int length = ((PSTUNMessageHeader *)theArray)->msgLength;
357 PSTUNAttribute * attrib = GetFirstAttribute();
358 while (length > 0) {
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();
382 while (length > 0) {
383 if (attrib->type == attribute.type) {
384 if (attrib->length == attribute.length)
385 *attrib = attribute;
386 else {
387 // More here
389 return;
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();
403 while (length > 0) {
404 if (attrib->type == type)
405 return attrib;
407 length -= attrib->length + 4;
408 attrib = attrib->GetNext();
410 return NULL;
414 bool Read(PUDPSocket & socket)
416 if (!socket.Read(GetPointer(1000), 1000))
417 return false;
418 SetSize(socket.GetLastReadCount());
419 return true;
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))
431 break;
433 if (Read(socket) && Validate() &&
434 memcmp(request->transactionId, (*this)->transactionId, sizeof(request->transactionId)) == 0)
435 return true;
438 return false;
443 bool PSTUNClient::OpenSocket(PUDPSocket & socket, PortInfo & portInfo) const
445 PWaitAndSignal mutex(portInfo.mutex);
447 WORD startPort = portInfo.currentPort;
449 do {
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);
457 return true;
460 } while (portInfo.currentPort != startPort);
462 PTRACE(1, "STUN\tFailed to bind to local UDP port in range "
463 << portInfo.currentPort << '-' << portInfo.maxPort);
464 return false;
468 PSTUNClient::NatTypes PSTUNClient::GetNatType(BOOL force)
470 if (!force && natType != UnknownNat)
471 return natType;
473 PUDPSocket socket;
474 if (!OpenSocket(socket, singlePortInfo))
475 return natType = UnknownNat;
477 // RFC3489 discovery
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);
513 if (notNAT) {
514 // Is not NAT or symmetric firewall
515 return natType = (testII ? OpenNat : SymmetricFirewall);
518 if (testII)
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] = {
558 "Unknown NAT",
559 "Open NAT",
560 "Cone NAT",
561 "Restricted NAT",
562 "Port Restricted NAT",
563 "Symmetric NAT",
564 "Symmetric Firewall",
565 "Blocked",
566 "Partially Blocked"
569 return Names[GetNatType(force)];
573 PSTUNClient::RTPSupportTypes PSTUNClient::IsSupportingRTP(BOOL force)
575 switch (GetNatType(force)) {
577 // types that do support RTP
578 case OpenNat:
579 case ConeNat:
580 return RTPOK;
582 // types that support RTP if media sent first
583 case SymmetricFirewall:
584 case RestrictedNat:
585 case PortRestrictedNat:
586 return RTPIfSendMedia;
588 // types that do not support RTP
589 case BlockedNat:
590 case SymmetricNat:
591 return RTPUnsupported;
593 // types that have unknown RTP support
594 case UnknownNat:
595 case PartialBlockedNat:
596 default:
597 break;
600 return RTPUnknown;
603 BOOL PSTUNClient::GetExternalAddress(PIPSocket::Address & externalAddress,
604 const PTimeInterval & maxAge)
606 if (cachedExternalAddress.IsValid() && (PTime() - timeAddressObtained > maxAge)) {
607 externalAddress = cachedExternalAddress;
608 return TRUE;
611 externalAddress = 0; // Set to invalid address
613 PUDPSocket socket;
614 if (!OpenSocket(socket, singlePortInfo))
615 return false;
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.");
623 return false;
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);
630 return false;
634 externalAddress = cachedExternalAddress = mappedAddress->GetIP();
635 timeAddressObtained = PTime();
636 return true;
640 BOOL PSTUNClient::CreateSocket(PUDPSocket * & socket)
642 socket = NULL;
644 switch (GetNatType(FALSE)) {
645 case ConeNat :
646 case RestrictedNat :
647 case PortRestrictedNat :
648 break;
650 case SymmetricNat :
651 if (singlePortInfo.basePort == 0 || singlePortInfo.basePort > singlePortInfo.maxPort)
653 PTRACE(1, "STUN\tInvalid local UDP port range "
654 << singlePortInfo.currentPort << '-' << singlePortInfo.maxPort);
655 return FALSE;
657 break;
659 default : // UnknownNet, SymmetricFirewall, BlockedNat
660 PTRACE(1, "STUN\tCannot create socket using NAT type " << GetNatTypeName());
661 return FALSE;
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);
681 socket = stunSocket;
682 return true;
685 PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress << ':' << serverPort);
687 else
688 PTRACE(1, "STUN\tServer " << serverAddress << ':' << serverPort << " unexpectedly went offline.");
691 delete stunSocket;
692 return false;
696 BOOL PSTUNClient::CreateSocketPair(PUDPSocket * & socket1,
697 PUDPSocket * & socket2)
699 socket1 = NULL;
700 socket2 = NULL;
702 switch (GetNatType(FALSE)) {
703 case ConeNat :
704 case RestrictedNat :
705 case PortRestrictedNat :
706 break;
708 case SymmetricNat :
709 if (pairedPortInfo.basePort == 0 || pairedPortInfo.basePort > pairedPortInfo.maxPort)
711 PTRACE(1, "STUN\tInvalid local UDP port range "
712 << pairedPortInfo.currentPort << '-' << pairedPortInfo.maxPort);
713 return FALSE;
715 break;
717 default : // UnknownNet, SymmetricFirewall, BlockedNat
718 PTRACE(1, "STUN\tCannot create socket pair using NAT type " << GetNatTypeName());
719 return FALSE;
722 int i;
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))
732 return false;
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.");
745 return false;
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);
755 return false;
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();
778 return true;
783 PTRACE(2, "STUN\tCould not get a pair of adjacent port numbers from NAT");
784 return false;
788 ////////////////////////////////////////////////////////////////
790 PSTUNUDPSocket::PSTUNUDPSocket()
791 : externalIP(0)
796 BOOL PSTUNUDPSocket::GetLocalAddress(Address & addr)
798 if (!externalIP.IsValid())
799 return PUDPSocket::GetLocalAddress(addr);
801 addr = externalIP;
802 return true;
806 BOOL PSTUNUDPSocket::GetLocalAddress(Address & addr, WORD & port)
808 if (!externalIP.IsValid())
809 return PUDPSocket::GetLocalAddress(addr, port);
811 addr = externalIP;
812 port = GetPort();
813 return true;
817 // End of File ////////////////////////////////////////////////////////////////