📄 pstun.cxx
字号:
memcmp(request->transactionId, (*this)->transactionId, sizeof(request->transactionId)) == 0)
return true;
}
return false;
}
};
bool PSTUNClient::OpenSocket(PUDPSocket & socket, PortInfo & portInfo) const
{
PWaitAndSignal mutex(portInfo.mutex);
WORD startPort = portInfo.currentPort;
do {
portInfo.currentPort++;
if (portInfo.currentPort > portInfo.maxPort)
portInfo.currentPort = portInfo.basePort;
if (socket.Listen(1, portInfo.currentPort)) {
socket.SetSendAddress(serverAddress, serverPort);
socket.SetReadTimeout(replyTimeout);
return true;
}
} while (portInfo.currentPort != startPort);
PTRACE(1, "STUN\tFailed to bind to local UDP port in range "
<< portInfo.currentPort << '-' << portInfo.maxPort);
return false;
}
PSTUNClient::NatTypes PSTUNClient::GetNatType(BOOL force)
{
if (!force && natType != UnknownNat)
return natType;
PUDPSocket socket;
if (!OpenSocket(socket, singlePortInfo))
return natType = UnknownNat;
// RFC3489 discovery
/* test I - the client sends a STUN Binding Request to a server, without
any flags set in the CHANGE-REQUEST attribute, and without the
RESPONSE-ADDRESS attribute. This causes the server to send the response
back to the address and port that the request came from. */
PSTUNMessage requestI(PSTUNMessage::BindingRequest);
requestI.AddAttribute(PSTUNChangeRequest(false, false));
PSTUNMessage responseI;
if (!responseI.Poll(socket, requestI, pollRetries)) {
if (socket.GetErrorCode(PChannel::LastWriteError) != PChannel::NoError) {
PTRACE(1, "STUN\tError writing to server " << serverAddress << ':' << serverPort << " - " << socket.GetErrorText(PChannel::LastWriteError));
return natType = UnknownNat; // No response usually means blocked
}
PTRACE(3, "STUN\tNo response to server " << serverAddress << ':' << serverPort << " - " << socket.GetErrorText(PChannel::LastReadError));
return natType = BlockedNat; // No response usually means blocked
}
PSTUNMappedAddress * mappedAddress = (PSTUNMappedAddress *)responseI.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
if (mappedAddress == NULL) {
PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress << ':' << serverPort);
return natType = UnknownNat; // Protocol error
}
PIPSocket::Address mappedAddressI = mappedAddress->GetIP();
WORD mappedPortI = mappedAddress->port;
bool notNAT = socket.GetPort() == mappedPortI && PIPSocket::IsLocalHost(mappedAddressI);
/* Test II - the client sends a Binding Request with both the "change IP"
and "change port" flags from the CHANGE-REQUEST attribute set. */
PSTUNMessage requestII(PSTUNMessage::BindingRequest);
requestII.AddAttribute(PSTUNChangeRequest(true, true));
PSTUNMessage responseII;
bool testII = responseII.Poll(socket, requestII, pollRetries);
if (notNAT) {
// Is not NAT or symmetric firewall
return natType = (testII ? OpenNat : SymmetricFirewall);
}
if (testII)
return natType = ConeNat;
PSTUNChangedAddress * changedAddress = (PSTUNChangedAddress *)responseI.FindAttribute(PSTUNAttribute::CHANGED_ADDRESS);
if (changedAddress == NULL)
return natType = UnknownNat; // Protocol error
// Send test I to another server, to see if restricted or symmetric
PIPSocket::Address secondaryServer = changedAddress->GetIP();
WORD secondaryPort = changedAddress->port;
socket.SetSendAddress(secondaryServer, secondaryPort);
PSTUNMessage requestI2(PSTUNMessage::BindingRequest);
requestI2.AddAttribute(PSTUNChangeRequest(false, false));
PSTUNMessage responseI2;
if (!responseI2.Poll(socket, requestI2, pollRetries)) {
PTRACE(2, "STUN\tPoll of secondary server " << secondaryServer << ':' << secondaryPort
<< " failed, NAT partially blocked by firwall rules.");
return natType = PartialBlockedNat;
}
mappedAddress = (PSTUNMappedAddress *)responseI2.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
if (mappedAddress == NULL) {
PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress << ':' << serverPort);
return UnknownNat; // Protocol error
}
if (mappedAddress->port != mappedPortI || mappedAddress->GetIP() != mappedAddressI)
return natType = SymmetricNat;
socket.SetSendAddress(serverAddress, serverPort);
PSTUNMessage requestIII(PSTUNMessage::BindingRequest);
requestIII.SetAttribute(PSTUNChangeRequest(false, true));
PSTUNMessage responseIII;
return natType = (responseIII.Poll(socket, requestIII, pollRetries) ? RestrictedNat : PortRestrictedNat);
}
PString PSTUNClient::GetNatTypeString(NatTypes type)
{
static const char * const Names[NumNatTypes] = {
"Unknown NAT",
"Open NAT",
"Cone NAT",
"Restricted NAT",
"Port Restricted NAT",
"Symmetric NAT",
"Symmetric Firewall",
"Blocked",
"Partially Blocked"
};
if (type < NumNatTypes)
return Names[type];
return psprintf("<NATType %u>", type);
}
PSTUNClient::RTPSupportTypes PSTUNClient::IsSupportingRTP(BOOL force)
{
switch (GetNatType(force)) {
// types that do support RTP
case OpenNat:
case ConeNat:
return RTPOK;
// types that support RTP if media sent first
case SymmetricFirewall:
case RestrictedNat:
case PortRestrictedNat:
return RTPIfSendMedia;
// types that do not support RTP
case BlockedNat:
case SymmetricNat:
return RTPUnsupported;
// types that have unknown RTP support
case UnknownNat:
case PartialBlockedNat:
default:
break;
}
return RTPUnknown;
}
BOOL PSTUNClient::GetExternalAddress(PIPSocket::Address & externalAddress,
const PTimeInterval & maxAge)
{
if (cachedExternalAddress.IsValid() && (PTime() - timeAddressObtained < maxAge)) {
externalAddress = cachedExternalAddress;
return TRUE;
}
externalAddress = 0; // Set to invalid address
PUDPSocket socket;
if (!OpenSocket(socket, singlePortInfo))
return false;
PSTUNMessage request(PSTUNMessage::BindingRequest);
request.AddAttribute(PSTUNChangeRequest(false, false));
PSTUNMessage response;
if (!response.Poll(socket, request, pollRetries))
{
PTRACE(1, "STUN\tServer " << serverAddress << ':' << serverPort << " unexpectedly went offline.");
return false;
}
PSTUNMappedAddress * mappedAddress = (PSTUNMappedAddress *)response.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
if (mappedAddress == NULL)
{
PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress << ':' << serverPort);
return false;
}
externalAddress = cachedExternalAddress = mappedAddress->GetIP();
timeAddressObtained = PTime();
return true;
}
BOOL PSTUNClient::CreateSocket(PUDPSocket * & socket)
{
socket = NULL;
switch (GetNatType(FALSE)) {
case ConeNat :
case RestrictedNat :
case PortRestrictedNat :
break;
case SymmetricNat :
if (singlePortInfo.basePort == 0 || singlePortInfo.basePort > singlePortInfo.maxPort)
{
PTRACE(1, "STUN\tInvalid local UDP port range "
<< singlePortInfo.currentPort << '-' << singlePortInfo.maxPort);
return FALSE;
}
break;
default : // UnknownNet, SymmetricFirewall, BlockedNat
PTRACE(1, "STUN\tCannot create socket using NAT type " << GetNatTypeName());
return FALSE;
}
PSTUNUDPSocket * stunSocket = new PSTUNUDPSocket;
if (OpenSocket(*stunSocket, singlePortInfo))
{
PSTUNMessage request(PSTUNMessage::BindingRequest);
request.AddAttribute(PSTUNChangeRequest(false, false));
PSTUNMessage response;
if (response.Poll(*stunSocket, request, pollRetries))
{
PSTUNMappedAddress * mappedAddress = (PSTUNMappedAddress *)response.FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
if (mappedAddress != NULL)
{
stunSocket->externalIP = mappedAddress->GetIP();
if (GetNatType(FALSE) != SymmetricNat)
stunSocket->port = mappedAddress->port;
stunSocket->SetSendAddress(0, 0);
stunSocket->SetReadTimeout(PMaxTimeInterval);
socket = stunSocket;
return true;
}
PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress << ':' << serverPort);
}
else
PTRACE(1, "STUN\tServer " << serverAddress << ':' << serverPort << " unexpectedly went offline.");
}
delete stunSocket;
return false;
}
BOOL PSTUNClient::CreateSocketPair(PUDPSocket * & socket1,
PUDPSocket * & socket2)
{
socket1 = NULL;
socket2 = NULL;
switch (GetNatType(FALSE)) {
case ConeNat :
case RestrictedNat :
case PortRestrictedNat :
break;
case SymmetricNat :
if (pairedPortInfo.basePort == 0 || pairedPortInfo.basePort > pairedPortInfo.maxPort)
{
PTRACE(1, "STUN\tInvalid local UDP port range "
<< pairedPortInfo.currentPort << '-' << pairedPortInfo.maxPort);
return FALSE;
}
break;
default : // UnknownNet, SymmetricFirewall, BlockedNat
PTRACE(1, "STUN\tCannot create socket pair using NAT type " << GetNatTypeName());
return FALSE;
}
PINDEX i;
PList<PSTUNUDPSocket> stunSocket;
PList<PSTUNMessage> request;
PList<PSTUNMessage> response;
for (i = 0; i < numSocketsForPairing; i++)
{
PINDEX idx = stunSocket.Append(new PSTUNUDPSocket);
if (!OpenSocket(stunSocket[idx], pairedPortInfo))
return false;
idx = request.Append(new PSTUNMessage(PSTUNMessage::BindingRequest));
request[idx].AddAttribute(PSTUNChangeRequest(false, false));
response.Append(new PSTUNMessage);
}
for (i = 0; i < numSocketsForPairing; i++)
{
if (!response[i].Poll(stunSocket[i], request[i], pollRetries))
{
PTRACE(1, "STUN\tServer " << serverAddress << ':' << serverPort << " unexpectedly went offline.");
return false;
}
}
for (i = 0; i < numSocketsForPairing; i++)
{
PSTUNMappedAddress * mappedAddress = (PSTUNMappedAddress *)response[i].FindAttribute(PSTUNAttribute::MAPPED_ADDRESS);
if (mappedAddress == NULL)
{
PTRACE(2, "STUN\tExpected mapped address attribute from server " << serverAddress << ':' << serverPort);
return false;
}
if (GetNatType(FALSE) != SymmetricNat)
stunSocket[i].port = mappedAddress->port;
stunSocket[i].externalIP = mappedAddress->GetIP();
}
for (i = 0; i < numSocketsForPairing; i++)
{
for (PINDEX j = 0; j < numSocketsForPairing; j++)
{
if ((stunSocket[i].port&1) == 0 && (stunSocket[i].port+1) == stunSocket[j].port) {
stunSocket[i].SetSendAddress(0, 0);
stunSocket[i].SetReadTimeout(PMaxTimeInterval);
stunSocket[j].SetSendAddress(0, 0);
stunSocket[j].SetReadTimeout(PMaxTimeInterval);
socket1 = &stunSocket[i];
socket2 = &stunSocket[j];
stunSocket.DisallowDeleteObjects();
stunSocket.Remove(socket1);
stunSocket.Remove(socket2);
stunSocket.AllowDeleteObjects();
return true;
}
}
}
PTRACE(2, "STUN\tCould not get a pair of adjacent port numbers from NAT");
return false;
}
BOOL PSTUNClient::IsAvailable()
{
switch (GetNatType(FALSE)) {
case ConeNat :
case RestrictedNat :
case PortRestrictedNat :
break;
case SymmetricNat :
if (pairedPortInfo.basePort == 0 || pairedPortInfo.basePort > pairedPortInfo.maxPort)
return FALSE;
break;
default : // UnknownNet, SymmetricFirewall, BlockedNat
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////
PSTUNUDPSocket::PSTUNUDPSocket()
: externalIP(0)
{
}
BOOL PSTUNUDPSocket::GetLocalAddress(Address & addr)
{
if (!externalIP.IsValid())
return PUDPSocket::GetLocalAddress(addr);
addr = externalIP;
return true;
}
BOOL PSTUNUDPSocket::GetLocalAddress(Address & addr, WORD & port)
{
if (!externalIP.IsValid())
return PUDPSocket::GetLocalAddress(addr, port);
addr = externalIP;
port = GetPort();
return true;
}
// End of File ////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -