📄 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.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 (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() && memcmp(request->transactionId, (*this)->transactionId, sizeof(request->transactionId)) == 0) return true; } return false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -