1 /*
2 * pstun.cxx
3 *
4 * STUN Client
5 *
6 * Portable Windows Library
7 *
8 * Copyright (c) 2003 Equivalence Pty. Ltd.
9 *
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/
14 *
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.
19 *
20 * The Original Code is Portable Windows Library.
21 *
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23 *
24 * Contributor(s): ______________________________________.
25 *
26 * $Revision: 26549 $
27 * $Author: rjongbloed $
28 * $Date: 2011-10-05 23:24:38 -0500 (Wed, 05 Oct 2011) $
29 */
30
31 #ifdef __GNUC__
32 #pragma implementation "pstun.h"
33 #endif
34
35 #include <ptlib.h>
36 #include <ptclib/pstun.h>
37 #include <ptclib/random.h>
38
39 #define new PNEW
40
41
42 // Sample server is at larry.gloo.net
43
44 #define DEFAULT_REPLY_TIMEOUT 800
45 #define DEFAULT_POLL_RETRIES 3
46 #define DEFAULT_NUM_SOCKETS_FOR_PAIRING 4
47
48
49 typedef PSTUNClient PNatMethod_STUN;
50 PCREATE_NAT_PLUGIN(STUN);
51
52 PFACTORY_CREATE(PFactory<PNatMethod>, PSTUNClient, "STUN");
53
54
55 ///////////////////////////////////////////////////////////////////////
56
PSTUNClient()57 PSTUNClient::PSTUNClient()
58 : serverPort(DefaultPort),
59 replyTimeout(DEFAULT_REPLY_TIMEOUT),
60 pollRetries(DEFAULT_POLL_RETRIES),
61 numSocketsForPairing(DEFAULT_NUM_SOCKETS_FOR_PAIRING),
62 natType(UnknownNat),
63 cachedExternalAddress(0),
64 timeAddressObtained(0)
65 {
66 }
67
PSTUNClient(const PString & server,WORD portBase,WORD portMax,WORD portPairBase,WORD portPairMax)68 PSTUNClient::PSTUNClient(const PString & server,
69 WORD portBase, WORD portMax,
70 WORD portPairBase, WORD portPairMax)
71 : serverPort(DefaultPort),
72 replyTimeout(DEFAULT_REPLY_TIMEOUT),
73 pollRetries(DEFAULT_POLL_RETRIES),
74 numSocketsForPairing(DEFAULT_NUM_SOCKETS_FOR_PAIRING),
75 natType(UnknownNat),
76 cachedExternalAddress(0),
77 timeAddressObtained(0)
78 {
79 SetServer(server);
80 SetPortRanges(portBase, portMax, portPairBase, portPairMax);
81 }
82
83
PSTUNClient(const PIPSocket::Address & address,WORD port,WORD portBase,WORD portMax,WORD portPairBase,WORD portPairMax)84 PSTUNClient::PSTUNClient(const PIPSocket::Address & address, WORD port,
85 WORD portBase, WORD portMax,
86 WORD portPairBase, WORD portPairMax)
87 : serverHost(address.AsString()),
88 serverPort(port),
89 replyTimeout(DEFAULT_REPLY_TIMEOUT),
90 pollRetries(DEFAULT_POLL_RETRIES),
91 numSocketsForPairing(DEFAULT_NUM_SOCKETS_FOR_PAIRING),
92 natType(UnknownNat),
93 cachedExternalAddress(0),
94 timeAddressObtained(0)
95 {
96 SetPortRanges(portBase, portMax, portPairBase, portPairMax);
97 }
98
99
Initialise(const PString & server,WORD portBase,WORD portMax,WORD portPairBase,WORD portPairMax)100 void PSTUNClient::Initialise(const PString & server,
101 WORD portBase, WORD portMax,
102 WORD portPairBase, WORD portPairMax)
103 {
104 SetServer(server);
105 SetPortRanges(portBase, portMax, portPairBase, portPairMax);
106 }
107
108
GetServerAddress(PIPSocket::Address & address,WORD & port) const109 bool PSTUNClient::GetServerAddress(PIPSocket::Address & address, WORD & port) const
110 {
111 if (serverPort == 0)
112 return false;
113
114 port = serverPort;
115
116 if (cachedServerAddress.IsValid()) {
117 address = cachedServerAddress;
118 return true;
119 }
120
121 return PIPSocket::GetHostAddress(serverHost, address);
122 }
123
124
SetServer(const PString & server)125 PBoolean PSTUNClient::SetServer(const PString & server)
126 {
127 PString host;
128 WORD port = serverPort;
129
130 PINDEX colon = server.Find(':');
131 if (colon == P_MAX_INDEX)
132 host = server;
133 else {
134 host = server.Left(colon);
135 PString service = server.Mid(colon+1);
136 if ((port = PIPSocket::GetPortByService("udp", service)) == 0) {
137 PTRACE(2, "STUN\tCould not find service \"" << service << "\".");
138 return false;
139 }
140 }
141
142 if (host.IsEmpty() || port == 0)
143 return false;
144
145 if (serverHost == host && serverPort == port)
146 return true;
147
148 serverHost = host;
149 serverPort = port;
150 InvalidateCache();
151 return true;
152 }
153
154
SetServer(const PIPSocket::Address & address,WORD port)155 PBoolean PSTUNClient::SetServer(const PIPSocket::Address & address, WORD port)
156 {
157 if (!address.IsValid() || port == 0)
158 return false;
159
160 serverHost = address.AsString();
161 cachedServerAddress = address;
162 serverPort = port;
163 return true;
164 }
165
166 #pragma pack(1)
167
168 struct PSTUNAttribute
169 {
170 enum Types {
171 MAPPED_ADDRESS = 0x0001,
172 RESPONSE_ADDRESS = 0x0002,
173 CHANGE_REQUEST = 0x0003,
174 SOURCE_ADDRESS = 0x0004,
175 CHANGED_ADDRESS = 0x0005,
176 USERNAME = 0x0006,
177 PASSWORD = 0x0007,
178 MESSAGE_INTEGRITY = 0x0008,
179 ERROR_CODE = 0x0009,
180 UNKNOWN_ATTRIBUTES = 0x000a,
181 REFLECTED_FROM = 0x000b,
182 MaxValidCode
183 };
184
185 PUInt16b type;
186 PUInt16b length;
187
GetNextPSTUNAttribute188 PSTUNAttribute * GetNext() const { return (PSTUNAttribute *)(((const BYTE *)this)+length+4); }
189 };
190
191 class PSTUNAddressAttribute : public PSTUNAttribute
192 {
193 public:
194 BYTE pad;
195 BYTE family;
196 PUInt16b port;
197 BYTE ip[4];
198
GetIP() const199 PIPSocket::Address GetIP() const { return PIPSocket::Address(4, ip); }
200
201 protected:
202 enum { SizeofAddressAttribute = sizeof(BYTE)+sizeof(BYTE)+sizeof(WORD)+sizeof(PIPSocket::Address) };
InitAddrAttr(Types newType)203 void InitAddrAttr(Types newType)
204 {
205 type = (WORD)newType;
206 length = SizeofAddressAttribute;
207 pad = 0;
208 family = 1;
209 }
IsValidAddrAttr(Types checkType) const210 bool IsValidAddrAttr(Types checkType) const
211 {
212 return type == checkType && length == SizeofAddressAttribute;
213 }
214 };
215
216 class PSTUNMappedAddress : public PSTUNAddressAttribute
217 {
218 public:
Initialise()219 void Initialise() { InitAddrAttr(MAPPED_ADDRESS); }
IsValid() const220 bool IsValid() const { return IsValidAddrAttr(MAPPED_ADDRESS); }
221 };
222
223 class PSTUNChangedAddress : public PSTUNAddressAttribute
224 {
225 public:
Initialise()226 void Initialise() { InitAddrAttr(CHANGED_ADDRESS); }
IsValid() const227 bool IsValid() const { return IsValidAddrAttr(CHANGED_ADDRESS); }
228 };
229
230 class PSTUNChangeRequest : public PSTUNAttribute
231 {
232 public:
233 BYTE flags[4];
234
PSTUNChangeRequest()235 PSTUNChangeRequest() { }
236
PSTUNChangeRequest(bool changeIP,bool changePort)237 PSTUNChangeRequest(bool changeIP, bool changePort)
238 {
239 Initialise();
240 SetChangeIP(changeIP);
241 SetChangePort(changePort);
242 }
243
Initialise()244 void Initialise()
245 {
246 type = CHANGE_REQUEST;
247 length = sizeof(flags);
248 memset(flags, 0, sizeof(flags));
249 }
IsValid() const250 bool IsValid() const { return type == CHANGE_REQUEST && length == sizeof(flags); }
251
GetChangeIP() const252 bool GetChangeIP() const { return (flags[3]&4) != 0; }
SetChangeIP(bool on)253 void SetChangeIP(bool on) { if (on) flags[3] |= 4; else flags[3] &= ~4; }
254
GetChangePort() const255 bool GetChangePort() const { return (flags[3]&2) != 0; }
SetChangePort(bool on)256 void SetChangePort(bool on) { if (on) flags[3] |= 2; else flags[3] &= ~2; }
257 };
258
259 class PSTUNMessageIntegrity : public PSTUNAttribute
260 {
261 public:
262 BYTE hmac[20];
263
Initialise()264 void Initialise()
265 {
266 type = MESSAGE_INTEGRITY;
267 length = sizeof(hmac);
268 memset(hmac, 0, sizeof(hmac));
269 }
IsValid() const270 bool IsValid() const { return type == MESSAGE_INTEGRITY && length == sizeof(hmac); }
271 };
272
273 struct PSTUNMessageHeader
274 {
275 PUInt16b msgType;
276 PUInt16b msgLength;
277 BYTE transactionId[16];
278 };
279
280
281 #pragma pack()
282
283
284 class PSTUNMessage : public PBYTEArray
285 {
286 public:
287 enum MsgType {
288 BindingRequest = 0x0001,
289 BindingResponse = 0x0101,
290 BindingError = 0x0111,
291
292 SharedSecretRequest = 0x0002,
293 SharedSecretResponse = 0x0102,
294 SharedSecretError = 0x0112,
295 };
296
PSTUNMessage()297 PSTUNMessage()
298 { }
299
PSTUNMessage(MsgType newType,const BYTE * id=NULL)300 PSTUNMessage(MsgType newType, const BYTE * id = NULL)
301 : PBYTEArray(sizeof(PSTUNMessageHeader))
302 {
303 SetType(newType, id);
304 }
305
SetType(MsgType newType,const BYTE * id=NULL)306 void SetType(MsgType newType, const BYTE * id = NULL)
307 {
308 SetMinSize(sizeof(PSTUNMessageHeader));
309 PSTUNMessageHeader * hdr = (PSTUNMessageHeader *)theArray;
310 hdr->msgType = (WORD)newType;
311 for (PINDEX i = 0; i < ((PINDEX)sizeof(hdr->transactionId)); i++)
312 hdr->transactionId[i] = id != NULL ? id[i] : (BYTE)PRandom::Number();
313 }
314
operator ->() const315 const PSTUNMessageHeader * operator->() const { return (PSTUNMessageHeader *)theArray; }
316
GetFirstAttribute()317 PSTUNAttribute * GetFirstAttribute() {
318
319 int length = ((PSTUNMessageHeader *)theArray)->msgLength;
320 if (theArray == NULL || length < (int) sizeof(PSTUNMessageHeader))
321 return NULL;
322
323 PSTUNAttribute * attr = (PSTUNAttribute *)(theArray+sizeof(PSTUNMessageHeader));
324 PSTUNAttribute * ptr = attr;
325
326 if (attr->length > GetSize() || attr->type >= PSTUNAttribute::MaxValidCode)
327 return NULL;
328
329 while (ptr && (BYTE*) ptr < (BYTE*)(theArray+GetSize()) && length >= (int) ptr->length+4) {
330
331 length -= ptr->length + 4;
332 ptr = ptr->GetNext();
333 }
334
335 if (length != 0)
336 return NULL;
337
338 return attr;
339 }
340
Validate(const PSTUNMessage & request)341 bool Validate(const PSTUNMessage & request)
342 {
343 int length = ((PSTUNMessageHeader *)theArray)->msgLength;
344 PSTUNAttribute * attrib = GetFirstAttribute();
345 while (attrib && length > 0) {
346 length -= attrib->length + 4;
347 attrib = attrib->GetNext();
348 }
349
350 if (length != 0) {
351 PTRACE(2, "STUN\tInvalid reply packet received, incorrect attribute length.");
352 return false;
353 }
354
355 if (memcmp(request->transactionId, (*this)->transactionId, sizeof(request->transactionId)) != 0) {
356 PTRACE(2, "STUN\tInvalid reply packet received, transaction ID does not match.");
357 return false;
358 }
359
360 return true;
361 }
362
AddAttribute(const PSTUNAttribute & attribute)363 void AddAttribute(const PSTUNAttribute & attribute)
364 {
365 PSTUNMessageHeader * hdr = (PSTUNMessageHeader *)theArray;
366 int oldLength = hdr->msgLength;
367 int attrSize = attribute.length + 4;
368 int newLength = oldLength + attrSize;
369 hdr->msgLength = (WORD)newLength;
370 // hdr pointer may be invalidated by next statement
371 SetMinSize(newLength+sizeof(PSTUNMessageHeader));
372 memcpy(theArray+sizeof(PSTUNMessageHeader)+oldLength, &attribute, attrSize);
373 }
374
SetAttribute(const PSTUNAttribute & attribute)375 void SetAttribute(const PSTUNAttribute & attribute)
376 {
377 int length = ((PSTUNMessageHeader *)theArray)->msgLength;
378 PSTUNAttribute * attrib = GetFirstAttribute();
379 while (length > 0) {
380 if (attrib->type == attribute.type) {
381 if (attrib->length == attribute.length)
382 *attrib = attribute;
383 else {
384 // More here
385 }
386 return;
387 }
388
389 length -= attrib->length + 4;
390 attrib = attrib->GetNext();
391 }
392
393 AddAttribute(attribute);
394 }
395
FindAttribute(PSTUNAttribute::Types type)396 PSTUNAttribute * FindAttribute(PSTUNAttribute::Types type)
397 {
398 int length = ((PSTUNMessageHeader *)theArray)->msgLength;
399 PSTUNAttribute * attrib = GetFirstAttribute();
400 while (length > 0) {
401 if (attrib->type == type)
402 return attrib;
403
404 length -= attrib->length + 4;
405 attrib = attrib->GetNext();
406 }
407 return NULL;
408 }
409
410
Read(PUDPSocket & socket)411 bool Read(PUDPSocket & socket)
412 {
413 if (!socket.Read(GetPointer(1000), 1000))
414 return false;
415
416 SetSize(socket.GetLastReadCount());
417 return true;
418 }
419
Write(PUDPSocket & socket) const420 bool Write(PUDPSocket & socket) const
421 {
422 if (socket.Write(theArray, ((PSTUNMessageHeader *)theArray)->msgLength+sizeof(PSTUNMessageHeader)))
423 return true;
424
425 PTRACE(1, "STUN\tError writing to " << socket.GetSendAddress()
426 << " - " << socket.GetErrorText(PChannel::LastWriteError));
427 return false;
428 }
429
Poll(PUDPSocket & socket,const PSTUNMessage & request,PINDEX pollRetries)430 bool Poll(PUDPSocket & socket, const PSTUNMessage & request, PINDEX pollRetries)
431 {
432 for (PINDEX retry = 0; retry < pollRetries; retry++) {
433 if (!request.Write(socket))
434 return false;
435
436 if (Read(socket) && Validate(request))
437 return true;
438 }
439
440 PTRACE(5, "STUN\tNo response from " << socket.GetSendAddress() << " after " << pollRetries << " retries.");
441 return false;
442 }
443 };
444
445
OpenSocket(PUDPSocket & socket,PortInfo & portInfo,const PIPSocket::Address & binding)446 bool PSTUNClient::OpenSocket(PUDPSocket & socket, PortInfo & portInfo, const PIPSocket::Address & binding)
447 {
448 if (serverPort == 0) {
449 PTRACE(1, "STUN\tServer port not set.");
450 return false;
451 }
452
453 if (!PIPSocket::GetHostAddress(serverHost, cachedServerAddress) || !cachedServerAddress.IsValid()) {
454 PTRACE(2, "STUN\tCould not find host \"" << serverHost << "\".");
455 return false;
456 }
457
458 PWaitAndSignal mutex(portInfo.mutex);
459
460 WORD startPort = portInfo.currentPort;
461
462 do {
463 portInfo.currentPort++;
464 if (portInfo.currentPort > portInfo.maxPort)
465 portInfo.currentPort = portInfo.basePort;
466
467 if (socket.Listen(binding, 1, portInfo.currentPort)) {
468 socket.SetSendAddress(cachedServerAddress, serverPort);
469 socket.SetReadTimeout(replyTimeout);
470 return true;
471 }
472
473 } while (portInfo.currentPort != startPort);
474
475 PTRACE(1, "STUN\tFailed to bind to local UDP port in range "
476 << portInfo.currentPort << '-' << portInfo.maxPort);
477 return false;
478 }
479
480
GetNatType(PBoolean force)481 PSTUNClient::NatTypes PSTUNClient::GetNatType(PBoolean force)
482 {
483 if (!force && natType != UnknownNat)
484 return natType;
485
486 PList<PUDPSocket> sockets;
487
488 PIPSocket::InterfaceTable interfaces;
489 if (PIPSocket::GetInterfaceTable(interfaces)) {
490 for (PINDEX i = 0; i < interfaces.GetSize(); i++) {
491 PIPSocket::Address binding = interfaces[i].GetAddress();
492 if (!binding.IsLoopback() && binding.GetVersion() == 4) {
493 PUDPSocket * socket = new PUDPSocket;
494 if (OpenSocket(*socket, singlePortInfo, binding))
495 sockets.Append(socket);
496 else
497 delete socket;
498 }
499 }
500 if (interfaces.IsEmpty()) {
501 PTRACE(1, "STUN\tNo interfaces available to find STUN server.");
502 return natType = UnknownNat;
503 }
504 }
505 else {
506 PUDPSocket * socket = new PUDPSocket;
507 sockets.Append(socket);
508 if (!OpenSocket(*socket, singlePortInfo, PIPSocket::GetDefaultIpAny()))
509 return natType = UnknownNat;
510 }
511
512 // RFC3489 discovery
513
514 /* test I - the client sends a STUN Binding Request to a server, without
515 any flags set in the CHANGE-REQUEST attribute, and without the
516 RESPONSE-ADDRESS attribute. This causes the server to send the response
517 back to the address and port that the request came from. */
518 PSTUNMessage requestI(PSTUNMessage::BindingRequest);
519 requestI.AddAttribute(PSTUNChangeRequest(false, false));
520 PSTUNMessage responseI;
521
522 PUDPSocket * replySocket = NULL;
523
524 for (PINDEX retry = 0; retry < pollRetries; ++retry) {
525 PSocket::SelectList selectList;
526 for (PList<PUDPSocket>::iterator socket = sockets.begin(); socket != sockets.end(); ++socket) {
527 if (requestI.Write(*socket))
528 selectList += *socket;
529 }
530
531 if (selectList.IsEmpty())
532 return natType = UnknownNat; // Could not send on any interface!
533
534 PChannel::Errors error = PIPSocket::Select(selectList, replyTimeout);
535 if (error != PChannel::NoError) {
536 PTRACE(1, "STUN\tError in select - " << PChannel::GetErrorText(error));
537 return natType = UnknownNat;
538 }
539
540 if (!selectList.IsEmpty()) {
541 PUDPSocket & udp = (PUDPSocket &)selectList.front();
542 if (responseI.Read(udp) && responseI.Validate(requestI)) {
543 replySocket = &udp;
544 break;
545 }
546 }
547 }
548
549 if (replySocket == NULL) {
550 PTRACE(3, "STUN\tNo response to " << *this);
551 return natType = BlockedNat; // No response usually means blocked
552 }
553
554 replySocket->GetLocalAddress(interfaceAddress);
555
556 PSTUNMappedAddress * mappedAddress = (PSTUNMappedAddress *)responseI.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
557 if (mappedAddress == NULL) {
558 PTRACE(2, "STUN\tExpected mapped address attribute from " << *this);
559 return natType = UnknownNat; // Protocol error
560 }
561
562 PIPSocket::Address mappedAddressI = mappedAddress->GetIP();
563 WORD mappedPortI = mappedAddress->port;
564 bool notNAT = replySocket->GetPort() == mappedPortI && PIPSocket::IsLocalHost(mappedAddressI);
565
566 /* Test II - the client sends a Binding Request with both the "change IP"
567 and "change port" flags from the CHANGE-REQUEST attribute set. */
568 PSTUNMessage requestII(PSTUNMessage::BindingRequest);
569 requestII.AddAttribute(PSTUNChangeRequest(true, true));
570 PSTUNMessage responseII;
571 bool testII = responseII.Poll(*replySocket, requestII, pollRetries);
572
573 if (notNAT) {
574 // Is not NAT or symmetric firewall
575 return natType = (testII ? OpenNat : SymmetricFirewall);
576 }
577
578 cachedExternalAddress = mappedAddressI;
579 timeAddressObtained.SetCurrentTime();
580
581 if (testII)
582 return natType = ConeNat;
583
584 PSTUNChangedAddress * changedAddress = (PSTUNChangedAddress *)responseI.FindAttribute(PSTUNAttribute::CHANGED_ADDRESS);
585 if (changedAddress == NULL)
586 return natType = UnknownNat; // Protocol error
587
588 // Send test I to another server, to see if restricted or symmetric
589 PIPSocket::Address secondaryServer = changedAddress->GetIP();
590 WORD secondaryPort = changedAddress->port;
591 replySocket->SetSendAddress(secondaryServer, secondaryPort);
592 PSTUNMessage requestI2(PSTUNMessage::BindingRequest);
593 requestI2.AddAttribute(PSTUNChangeRequest(false, false));
594 PSTUNMessage responseI2;
595 if (!responseI2.Poll(*replySocket, requestI2, pollRetries)) {
596 PTRACE(2, "STUN\tPoll of secondary server " << secondaryServer << ':' << secondaryPort
597 << " failed, NAT partially blocked by firwall rules.");
598 return natType = PartialBlockedNat;
599 }
600
601 mappedAddress = (PSTUNMappedAddress *)responseI2.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
602 if (mappedAddress == NULL) {
603 PTRACE(2, "STUN\tExpected mapped address attribute from " << *this);
604 return UnknownNat; // Protocol error
605 }
606
607 if (mappedAddress->port != mappedPortI || mappedAddress->GetIP() != mappedAddressI)
608 return natType = SymmetricNat;
609
610 replySocket->SetSendAddress(cachedServerAddress, serverPort);
611 PSTUNMessage requestIII(PSTUNMessage::BindingRequest);
612 requestIII.SetAttribute(PSTUNChangeRequest(false, true));
613 PSTUNMessage responseIII;
614 return natType = (responseIII.Poll(*replySocket, requestIII, pollRetries) ? RestrictedNat : PortRestrictedNat);
615 }
616
617
GetNatTypeString(NatTypes type)618 PString PSTUNClient::GetNatTypeString(NatTypes type)
619 {
620 static const char * const Names[NumNatTypes] = {
621 "Unknown NAT",
622 "Open NAT",
623 "Cone NAT",
624 "Restricted NAT",
625 "Port Restricted NAT",
626 "Symmetric NAT",
627 "Symmetric Firewall",
628 "Blocked",
629 "Partially Blocked"
630 };
631
632 if (type < NumNatTypes)
633 return Names[type];
634
635 return psprintf("<NATType %u>", type);
636 }
637
638
GetRTPSupport(PBoolean force)639 PSTUNClient::RTPSupportTypes PSTUNClient::GetRTPSupport(PBoolean force)
640 {
641 switch (GetNatType(force)) {
642 // types that do support RTP
643 case OpenNat:
644 case ConeNat:
645 return RTPSupported;
646
647 // types that support RTP if media sent first
648 case SymmetricFirewall:
649 case RestrictedNat:
650 case PortRestrictedNat:
651 return RTPIfSendMedia;
652
653 // types that do not support RTP
654 case BlockedNat:
655 case SymmetricNat:
656 return RTPUnsupported;
657
658 // types that have unknown RTP support
659 default:
660 return RTPUnknown;
661 }
662 }
663
GetExternalAddress(PIPSocket::Address & externalAddress,const PTimeInterval & maxAge)664 PBoolean PSTUNClient::GetExternalAddress(PIPSocket::Address & externalAddress,
665 const PTimeInterval & maxAge)
666 {
667 if (cachedExternalAddress.IsValid() && (PTime() - timeAddressObtained < maxAge)) {
668 externalAddress = cachedExternalAddress;
669 return PTrue;
670 }
671
672 externalAddress = 0; // Set to invalid address
673
674 PUDPSocket socket;
675 if (!OpenSocket(socket, singlePortInfo, PIPSocket::GetDefaultIpAny()))
676 return false;
677
678 PSTUNMessage request(PSTUNMessage::BindingRequest);
679 request.AddAttribute(PSTUNChangeRequest(false, false));
680 PSTUNMessage response;
681 if (!response.Poll(socket, request, pollRetries))
682 {
683 PTRACE(1, "STUN\t" << *this << " unexpectedly went offline getting external address.");
684 return false;
685 }
686
687 PSTUNMappedAddress * mappedAddress = (PSTUNMappedAddress *)response.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
688 if (mappedAddress == NULL)
689 {
690 PTRACE(2, "STUN\tExpected mapped address attribute from " << *this);
691 return false;
692 }
693
694
695 externalAddress = cachedExternalAddress = mappedAddress->GetIP();
696 timeAddressObtained.SetCurrentTime();
697 return true;
698 }
699
700
GetInterfaceAddress(PIPSocket::Address & internalAddress) const701 bool PSTUNClient::GetInterfaceAddress(PIPSocket::Address & internalAddress) const
702 {
703 if (!interfaceAddress.IsValid())
704 return false;
705
706 internalAddress = interfaceAddress;
707 return true;
708 }
709
710
InvalidateCache()711 void PSTUNClient::InvalidateCache()
712 {
713 cachedServerAddress = 0;
714 cachedExternalAddress = 0;
715 interfaceAddress = 0;
716 natType = UnknownNat;
717 }
718
719
CreateSocket(PUDPSocket * & socket,const PIPSocket::Address & binding,WORD localPort)720 PBoolean PSTUNClient::CreateSocket(PUDPSocket * & socket, const PIPSocket::Address & binding, WORD localPort)
721 {
722 socket = NULL;
723
724 switch (GetNatType(PFalse)) {
725 case OpenNat :
726 case ConeNat :
727 case RestrictedNat :
728 case PortRestrictedNat :
729 break;
730
731 case SymmetricNat :
732 if (localPort == 0 && (singlePortInfo.basePort == 0 || singlePortInfo.basePort > singlePortInfo.maxPort))
733 {
734 PTRACE(1, "STUN\tInvalid local UDP port range "
735 << singlePortInfo.currentPort << '-' << singlePortInfo.maxPort);
736 return PFalse;
737 }
738 break;
739
740 default : // UnknownNet, SymmetricFirewall, BlockedNat
741 PTRACE(1, "STUN\tCannot create socket using NAT type " << GetNatTypeName());
742 return PFalse;
743 }
744
745 if (!IsAvailable(binding)) {
746 PTRACE(1, "STUN\tCannot create socket using binding " << binding);
747 return PFalse;
748 }
749
750 PSTUNUDPSocket * stunSocket = new PSTUNUDPSocket;
751
752 PBoolean opened;
753 if (localPort == 0)
754 opened = OpenSocket(*stunSocket, singlePortInfo, interfaceAddress);
755 else {
756 PortInfo portInfo = localPort;
757 opened = OpenSocket(*stunSocket, portInfo, interfaceAddress);
758 }
759
760 if (opened)
761 {
762 PSTUNMessage request(PSTUNMessage::BindingRequest);
763 request.AddAttribute(PSTUNChangeRequest(false, false));
764 PSTUNMessage response;
765
766 if (response.Poll(*stunSocket, request, pollRetries))
767 {
768 PSTUNMappedAddress * mappedAddress = (PSTUNMappedAddress *)response.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
769 if (mappedAddress != NULL)
770 {
771 stunSocket->externalIP = mappedAddress->GetIP();
772 if (GetNatType(PFalse) != SymmetricNat)
773 stunSocket->port = mappedAddress->port;
774 stunSocket->SetSendAddress(0, 0);
775 stunSocket->SetReadTimeout(PMaxTimeInterval);
776 socket = stunSocket;
777 return true;
778 }
779
780 PTRACE(2, "STUN\tExpected mapped address attribute from " << *this);
781 }
782 else
783 PTRACE(1, "STUN\t" << *this << " unexpectedly went offline creating socket.");
784 }
785
786 delete stunSocket;
787 return false;
788 }
789
790
CreateSocketPair(PUDPSocket * & socket1,PUDPSocket * & socket2,const PIPSocket::Address & binding)791 PBoolean PSTUNClient::CreateSocketPair(PUDPSocket * & socket1,
792 PUDPSocket * & socket2,
793 const PIPSocket::Address & binding)
794 {
795 socket1 = NULL;
796 socket2 = NULL;
797
798 switch (GetNatType(PFalse)) {
799 case OpenNat :
800 case ConeNat :
801 case RestrictedNat :
802 case PortRestrictedNat :
803 break;
804
805 case SymmetricNat :
806 if (pairedPortInfo.basePort == 0 || pairedPortInfo.basePort > pairedPortInfo.maxPort)
807 {
808 PTRACE(1, "STUN\tInvalid local UDP port range "
809 << pairedPortInfo.currentPort << '-' << pairedPortInfo.maxPort);
810 return PFalse;
811 }
812 break;
813
814 default : // UnknownNet, SymmetricFirewall, BlockedNat
815 PTRACE(1, "STUN\tCannot create socket pair using NAT type " << GetNatTypeName());
816 return PFalse;
817 }
818
819 if (!IsAvailable(binding)) {
820 PTRACE(1, "STUN\tCannot create socket using binding " << binding);
821 return PFalse;
822 }
823
824 PINDEX i;
825
826 PArray<PSTUNUDPSocket> stunSocket;
827 PArray<PSTUNMessage> request;
828 PArray<PSTUNMessage> response;
829
830 for (i = 0; i < numSocketsForPairing; i++)
831 {
832 PINDEX idx = stunSocket.Append(new PSTUNUDPSocket);
833 if (!OpenSocket(stunSocket[idx], pairedPortInfo, interfaceAddress)) {
834 PTRACE(1, "STUN\tUnable to open socket to " << *this);
835 return false;
836 }
837
838 idx = request.Append(new PSTUNMessage(PSTUNMessage::BindingRequest));
839 request[idx].AddAttribute(PSTUNChangeRequest(false, false));
840
841 response.Append(new PSTUNMessage);
842 }
843
844 for (i = 0; i < numSocketsForPairing; i++)
845 {
846 if (!response[i].Poll(stunSocket[i], request[i], pollRetries))
847 {
848 PTRACE(1, "STUN\t" << *this << " unexpectedly went offline creating socket pair.");
849 return false;
850 }
851 }
852
853 for (i = 0; i < numSocketsForPairing; i++)
854 {
855 PSTUNMappedAddress * mappedAddress = (PSTUNMappedAddress *)response[i].FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
856 if (mappedAddress == NULL)
857 {
858 PTRACE(2, "STUN\tExpected mapped address attribute from " << *this);
859 return false;
860 }
861 if (GetNatType(PFalse) != SymmetricNat)
862 stunSocket[i].port = mappedAddress->port;
863 stunSocket[i].externalIP = mappedAddress->GetIP();
864 }
865
866 for (i = 0; i < numSocketsForPairing; i++)
867 {
868 for (PINDEX j = 0; j < numSocketsForPairing; j++)
869 {
870 if ((stunSocket[i].port&1) == 0 && (stunSocket[i].port+1) == stunSocket[j].port) {
871 stunSocket[i].SetSendAddress(0, 0);
872 stunSocket[i].SetReadTimeout(PMaxTimeInterval);
873 stunSocket[j].SetSendAddress(0, 0);
874 stunSocket[j].SetReadTimeout(PMaxTimeInterval);
875 socket1 = &stunSocket[i];
876 socket2 = &stunSocket[j];
877 stunSocket.DisallowDeleteObjects();
878 stunSocket.Remove(socket1);
879 stunSocket.Remove(socket2);
880 stunSocket.AllowDeleteObjects();
881 return true;
882 }
883 }
884 }
885
886 PTRACE(2, "STUN\tCould not get a pair of adjacent port numbers from NAT");
887 return false;
888 }
889
IsAvailable(const PIPSocket::Address & binding)890 bool PSTUNClient::IsAvailable(const PIPSocket::Address & binding)
891 {
892 switch (GetNatType(PFalse)) {
893 case ConeNat :
894 case RestrictedNat :
895 case PortRestrictedNat :
896 break;
897
898 case SymmetricNat :
899 if (pairedPortInfo.basePort == 0 || pairedPortInfo.basePort > pairedPortInfo.maxPort)
900 return false;
901 break;
902
903 default : // UnknownNet, SymmetricFirewall, BlockedNat
904 return false;
905 }
906
907 return binding.IsAny() || binding == interfaceAddress || binding == cachedExternalAddress;
908 }
909
910 ////////////////////////////////////////////////////////////////
911
PSTUNUDPSocket()912 PSTUNUDPSocket::PSTUNUDPSocket()
913 : externalIP(0)
914 {
915 }
916
917
GetLocalAddress(Address & addr)918 PBoolean PSTUNUDPSocket::GetLocalAddress(Address & addr)
919 {
920 if (!externalIP.IsValid())
921 return PUDPSocket::GetLocalAddress(addr);
922
923 addr = externalIP;
924 return true;
925 }
926
927
GetLocalAddress(Address & addr,WORD & port)928 PBoolean PSTUNUDPSocket::GetLocalAddress(Address & addr, WORD & port)
929 {
930 if (!externalIP.IsValid())
931 return PUDPSocket::GetLocalAddress(addr, port);
932
933 addr = externalIP;
934 port = GetPort();
935 return true;
936 }
937
938
939 // End of File ////////////////////////////////////////////////////////////////
940