📄 pstun.cxx
字号:
/*
* pstun.cxx
*
* STUN Client
*
* Portable Windows Library
*
* Copyright (c) 2003 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Contributor(s): ______________________________________.
*
* $Log: pstun.cxx,v $
* Revision 1.1 2006/06/29 04:18:03 joegenbaclor
* *** empty log message ***
*
* Revision 1.20 2005/12/05 21:58:36 dsandras
* Fixed bug when looking if the cache is still valid.
*
* Revision 1.19 2005/11/30 12:47:41 csoutheren
* Removed tabs, reformatted some code, and changed tags for Doxygen
*
* Revision 1.18 2005/07/13 11:15:26 csoutheren
* Backported NAT abstraction files from isvo branch
*
* Revision 1.17 2005/06/20 10:55:17 rjongbloed
* Changed the timeout and retries so if there is a blocking firewall it does not take 15 seconds to find out!
* Added access functions so timeout and retries are application configurable.
* Added function (and << operator) to get NAT type enum as string.
*
* Revision 1.16.4.1 2005/04/25 13:19:27 shorne
* Add Support for other NAT methods
*
* Revision 1.16 2004/11/25 07:23:46 csoutheren
* Added IsSupportingRTP function to simplify detecting when STUN supports RTP
*
* Revision 1.15 2004/10/26 05:58:23 csoutheren
* Increased timeout on STUN responses to avoid spurious STUN failures due
* to network trsffic/congestion etc
*
* Revision 1.14 2004/08/18 13:16:07 rjongbloed
* Fixed STUN CreateSocketPair so first socket is always even.
*
* Revision 1.13 2004/03/14 05:47:52 rjongbloed
* Fixed incorrect detection of symmetric NAT (eg Linux masquerading) and also
* some NAT systems which are partially blocked due to firewall rules.
*
* Revision 1.12 2004/02/24 11:15:48 rjongbloed
* Added function to get external router address, also did a bunch of documentation.
*
* Revision 1.11 2004/02/17 11:11:05 rjongbloed
* Added missing #pragma pack() to turn off byte alignment for the last class, thanks Ted Szoczei
*
* Revision 1.10 2004/01/17 17:54:02 rjongbloed
* Added function to get server name from STUN client.
*
* Revision 1.9 2003/10/08 22:00:18 dereksmithies
* Fix unsigned/signed warning message. Thanks to Craig Southeren.
*
* Revision 1.8 2003/10/05 00:56:25 rjongbloed
* Rewrite of STUN to not to use imported code with undesirable license.
*
* Revision 1.5 2003/02/05 06:26:49 robertj
* More work in making the STUN usable for Symmetric NAT systems.
*
* Revision 1.4 2003/02/04 07:02:17 robertj
* Added ip/port version of constructor.
* Removed creating sockets for Open type.
*
* Revision 1.3 2003/02/04 05:55:04 craigs
* Added socket pair function
*
* Revision 1.2 2003/02/04 05:06:24 craigs
* Added new functions
*
* Revision 1.1 2003/02/04 03:31:04 robertj
* Added STUN
*
*/
#ifdef __GNUC__
#pragma implementation "pstun.h"
#endif
#include <ptlib.h>
#include <ptclib/pstun.h>
#include <ptclib/random.h>
// Sample server is at larry.gloo.net
#define DEFAULT_REPLY_TIMEOUT 1000
#define DEFAULT_POLL_RETRIES 5
#define DEFAULT_NUM_SOCKETS_FOR_PAIRING 4
///////////////////////////////////////////////////////////////////////
PSTUNClient::PSTUNClient(const PString & server,
WORD portBase, WORD portMax,
WORD portPairBase, WORD portPairMax)
: serverAddress(0),
serverPort(DefaultPort),
replyTimeout(DEFAULT_REPLY_TIMEOUT),
pollRetries(DEFAULT_POLL_RETRIES),
numSocketsForPairing(DEFAULT_NUM_SOCKETS_FOR_PAIRING),
natType(UnknownNat),
cachedExternalAddress(0),
timeAddressObtained(0)
{
SetServer(server);
SetPortRanges(portBase, portMax, portPairBase, portPairMax);
}
PSTUNClient::PSTUNClient(const PIPSocket::Address & address, WORD port,
WORD portBase, WORD portMax,
WORD portPairBase, WORD portPairMax)
: serverAddress(address),
serverPort(port),
replyTimeout(DEFAULT_REPLY_TIMEOUT),
pollRetries(DEFAULT_POLL_RETRIES),
numSocketsForPairing(DEFAULT_NUM_SOCKETS_FOR_PAIRING),
natType(UnknownNat),
cachedExternalAddress(0),
timeAddressObtained(0)
{
SetPortRanges(portBase, portMax, portPairBase, portPairMax);
}
PString PSTUNClient::GetServer() const
{
PStringStream str;
str << serverAddress << ':' << serverPort;
return str;
}
BOOL PSTUNClient::SetServer(const PString & server)
{
PINDEX colon = server.Find(':');
if (colon == P_MAX_INDEX) {
if (!PIPSocket::GetHostAddress(server, serverAddress))
return FALSE;
}
else {
if (!PIPSocket::GetHostAddress(server.Left(colon), serverAddress))
return FALSE;
serverPort = PIPSocket::GetPortByService("udp", server.Mid(colon+1));
}
return serverAddress.IsValid() && serverPort != 0;
}
BOOL PSTUNClient::SetServer(const PIPSocket::Address & address, WORD port)
{
serverAddress = address;
serverPort = port;
return serverAddress.IsValid() && serverPort != 0;
}
#pragma pack(1)
struct PSTUNAttribute
{
enum Types {
MAPPED_ADDRESS = 0x0001,
RESPONSE_ADDRESS = 0x0002,
CHANGE_REQUEST = 0x0003,
SOURCE_ADDRESS = 0x0004,
CHANGED_ADDRESS = 0x0005,
USERNAME = 0x0006,
PASSWORD = 0x0007,
MESSAGE_INTEGRITY = 0x0008,
ERROR_CODE = 0x0009,
UNKNOWN_ATTRIBUTES = 0x000a,
REFLECTED_FROM = 0x000b,
};
PUInt16b type;
PUInt16b length;
PSTUNAttribute * GetNext() const { return (PSTUNAttribute *)(((const BYTE *)this)+length+4); }
};
class PSTUNAddressAttribute : public PSTUNAttribute
{
public:
BYTE pad;
BYTE family;
PUInt16b port;
BYTE ip[4];
PIPSocket::Address GetIP() const { return PIPSocket::Address(4, ip); }
protected:
enum { SizeofAddressAttribute = sizeof(BYTE)+sizeof(BYTE)+sizeof(WORD)+sizeof(PIPSocket::Address) };
void InitAddrAttr(Types newType)
{
type = (WORD)newType;
length = SizeofAddressAttribute;
pad = 0;
family = 1;
}
bool IsValidAddrAttr(Types checkType) const
{
return type == checkType && length == SizeofAddressAttribute;
}
};
class PSTUNMappedAddress : public PSTUNAddressAttribute
{
public:
void Initialise() { InitAddrAttr(MAPPED_ADDRESS); }
bool IsValid() const { return IsValidAddrAttr(MAPPED_ADDRESS); }
};
class PSTUNChangedAddress : public PSTUNAddressAttribute
{
public:
void Initialise() { InitAddrAttr(CHANGED_ADDRESS); }
bool IsValid() const { return IsValidAddrAttr(CHANGED_ADDRESS); }
};
class PSTUNChangeRequest : public PSTUNAttribute
{
public:
BYTE flags[4];
PSTUNChangeRequest() { }
PSTUNChangeRequest(bool changeIP, bool changePort)
{
Initialise();
SetChangeIP(changeIP);
SetChangePort(changePort);
}
void Initialise()
{
type = CHANGE_REQUEST;
length = sizeof(flags);
memset(flags, 0, sizeof(flags));
}
bool IsValid() const { return type == CHANGE_REQUEST && length == sizeof(flags); }
bool GetChangeIP() const { return (flags[3]&4) != 0; }
void SetChangeIP(bool on) { if (on) flags[3] |= 4; else flags[3] &= ~4; }
bool GetChangePort() const { return (flags[3]&2) != 0; }
void SetChangePort(bool on) { if (on) flags[3] |= 2; else flags[3] &= ~2; }
};
class PSTUNMessageIntegrity : public PSTUNAttribute
{
public:
BYTE hmac[20];
void Initialise()
{
type = MESSAGE_INTEGRITY;
length = sizeof(hmac);
memset(hmac, 0, sizeof(hmac));
}
bool IsValid() const { return type == MESSAGE_INTEGRITY && length == sizeof(hmac); }
};
struct PSTUNMessageHeader
{
PUInt16b msgType;
PUInt16b msgLength;
BYTE transactionId[16];
};
#pragma pack()
class PSTUNMessage : public PBYTEArray
{
public:
enum MsgType {
BindingRequest = 0x0001,
BindingResponse = 0x0101,
BindingError = 0x0111,
SharedSecretRequest = 0x0002,
SharedSecretResponse = 0x0102,
SharedSecretError = 0x0112,
};
PSTUNMessage()
{ }
PSTUNMessage(MsgType newType, const BYTE * id = NULL)
: PBYTEArray(sizeof(PSTUNMessageHeader))
{
SetType(newType, id);
}
void SetType(MsgType newType, const BYTE * id = NULL)
{
SetMinSize(sizeof(PSTUNMessageHeader));
PSTUNMessageHeader * hdr = (PSTUNMessageHeader *)theArray;
hdr->msgType = (WORD)newType;
for (PINDEX i = 0; i < ((PINDEX)sizeof(hdr->transactionId)); i++)
hdr->transactionId[i] = id != NULL ? id[i] : (BYTE)PRandom::Number();
}
const PSTUNMessageHeader * operator->() const { return (PSTUNMessageHeader *)theArray; }
PSTUNAttribute * GetFirstAttribute() { return (PSTUNAttribute *)(theArray+sizeof(PSTUNMessageHeader)); }
bool Validate()
{
int length = ((PSTUNMessageHeader *)theArray)->msgLength;
PSTUNAttribute * attrib = GetFirstAttribute();
while (attrib && length > 0) {
length -= attrib->length + 4;
attrib = attrib->GetNext();
}
return length == 0; // Exactly correct length
}
void AddAttribute(const PSTUNAttribute & attribute)
{
PSTUNMessageHeader * hdr = (PSTUNMessageHeader *)theArray;
int oldLength = hdr->msgLength;
int attrSize = attribute.length + 4;
int newLength = oldLength + attrSize;
hdr->msgLength = (WORD)newLength;
// hdr pointer may be invalidated by next statement
SetMinSize(newLength+sizeof(PSTUNMessageHeader));
memcpy(theArray+sizeof(PSTUNMessageHeader)+oldLength, &attribute, attrSize);
}
void SetAttribute(const PSTUNAttribute & attribute)
{
int length = ((PSTUNMessageHeader *)theArray)->msgLength;
PSTUNAttribute * attrib = GetFirstAttribute();
while (length > 0) {
if (attrib->type == attribute.type) {
if (attrib->length == attribute.length)
*attrib = attribute;
else {
// More here
}
return;
}
length -= attrib->length + 4;
attrib = attrib->GetNext();
}
AddAttribute(attribute);
}
PSTUNAttribute * FindAttribute(PSTUNAttribute::Types type)
{
int length = ((PSTUNMessageHeader *)theArray)->msgLength;
PSTUNAttribute * attrib = GetFirstAttribute();
while (length > 0) {
if (attrib->type == type)
return attrib;
length -= attrib->length + 4;
attrib = attrib->GetNext();
}
return NULL;
}
bool Read(PUDPSocket & socket)
{
if (!socket.Read(GetPointer(1000), 1000))
return false;
SetSize(socket.GetLastReadCount());
return true;
}
bool Write(PUDPSocket & socket) const
{
return socket.Write(theArray, ((PSTUNMessageHeader *)theArray)->msgLength+sizeof(PSTUNMessageHeader)) != FALSE;
}
bool Poll(PUDPSocket & socket, const PSTUNMessage & request, PINDEX pollRetries)
{
for (PINDEX retry = 0; retry < pollRetries; retry++) {
if (!request.Write(socket))
break;
if (Read(socket) && Validate() &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -