HRESULT CTestClientLogic::GetMappedAddressForDestinationAddress(const CSocketAddress& addrDest, CSocketAddress* pAddrMapped) { HRESULT hr = S_OK; if (addrDest.IsSameIP_and_Port(_addrServerPP)) { *pAddrMapped = _addrMappedPP; } else if (addrDest.IsSameIP_and_Port(_addrServerPA)) { *pAddrMapped = _addrMappedPA; } else if (addrDest.IsSameIP_and_Port(_addrServerAP)) { *pAddrMapped = _addrMappedAP; } else if (addrDest.IsSameIP_and_Port(_addrServerAA)) { *pAddrMapped = _addrMappedAA; } else { ASSERT(false); hr = E_FAIL; } return hr; }
HRESULT CTestMessageHandler::ValidateMappedAddress(CStunMessageReader& reader, const CSocketAddress& addrClient) { HRESULT hr = S_OK; StunTransactionId transid; CSocketAddress mappedaddr; CRefCountedBuffer spBuffer; Chk(reader.GetStream().GetBuffer(&spBuffer)); reader.GetTransactionId(&transid); //ChkA(reader.GetAttributeByType(STUN_ATTRIBUTE_XORMAPPEDADDRESS, &attrib)); //ChkA(GetXorMappedAddress(spBuffer->GetData()+attrib.offset, attrib.size, transid, &mappedaddr)); reader.GetXorMappedAddress(&mappedaddr); ChkIfA(false == addrClient.IsSameIP_and_Port(mappedaddr), E_FAIL); //ChkA(reader.GetAttributeByType(STUN_ATTRIBUTE_MAPPEDADDRESS, &attrib)); //ChkA(GetMappedAddress(spBuffer->GetData()+attrib.offset, attrib.size, &mappedaddr)); reader.GetMappedAddress(&mappedaddr); ChkIfA(false == addrClient.IsSameIP_and_Port(mappedaddr), E_FAIL); Cleanup: return hr; }
HRESULT CTestMessageHandler::ValidateMappedAddress(CStunMessageReader& reader, const CSocketAddress& addrExpected, bool fLegacyOnly) { HRESULT hr = S_OK; CSocketAddress addrMapped; CSocketAddress addrXorMapped; HRESULT hrResult; hrResult = reader.GetXorMappedAddress(&addrXorMapped); if (SUCCEEDED(hrResult)) { ChkIfA(false == addrExpected.IsSameIP_and_Port(addrXorMapped), E_FAIL); ChkIfA(fLegacyOnly, E_FAIL); // legacy responses should not include XOR mapped } else { ChkIfA(fLegacyOnly==false, E_FAIL); // non-legacy responses should include XOR Mapped address } ChkA(reader.GetMappedAddress(&addrMapped)); ChkIfA(false == addrExpected.IsSameIP_and_Port(addrMapped), E_FAIL); Cleanup: return hr; }
HRESULT CTestClientLogic::Test1() { HRESULT hr = S_OK; HRESULT hrTmp = 0; CStunClientLogic clientlogic; ::StunClientLogicConfig config; CRefCountedBuffer spMsgOut(new CBuffer(MAX_STUN_MESSAGE_SIZE)); CRefCountedBuffer spMsgIn(new CBuffer(MAX_STUN_MESSAGE_SIZE)); StunClientResults results; StunTransactionId transid; CSocketAddress addrDest; CSocketAddress addrServerPP = CSocketAddress(0xaaaaaaaa, 1001); CSocketAddress addrLocal = CSocketAddress(0xdddddddd, 4444); CSocketAddress addrMapped = CSocketAddress(0xeeeeeeee, 5555); config.addrServer = addrServerPP; config.fBehaviorTest = false; config.fFilteringTest = false; config.timeoutSeconds = 10; config.uMaxAttempts = 2; config.fTimeoutIsInstant = false; ChkA(clientlogic.Initialize(config)); ChkA(clientlogic.GetNextMessage(spMsgOut, &addrDest, 0)); // we expect to get back a message for the serverPP ChkIfA(addrDest.IsSameIP_and_Port(addrServerPP)==false, E_UNEXPECTED); // check to make sure out timeout logic appears to work hrTmp = clientlogic.GetNextMessage(spMsgOut, &addrDest, 1); ChkIfA(hrTmp != E_STUNCLIENT_STILL_WAITING, E_UNEXPECTED); // now we should get a dupe of what we had before ChkA(clientlogic.GetNextMessage(spMsgOut, &addrDest, 11000)); // the message should be a binding request ChkA(ValidateBindingRequest(spMsgOut, &transid)); // now let's generate a response ChkA(GenerateBindingResponseMessage(addrMapped, transid, spMsgIn)); ChkA(clientlogic.ProcessResponse(spMsgIn, addrServerPP, addrLocal)); // results should be ready hrTmp = clientlogic.GetNextMessage(spMsgOut, &addrDest, 12000); ChkIfA(hrTmp != E_STUNCLIENT_RESULTS_READY, E_UNEXPECTED); ChkA(clientlogic.GetResults(&results)); // results should have a successful binding result ChkIfA(results.fBindingTestSuccess==false, E_UNEXPECTED); ChkIfA(results.fIsDirect, E_UNEXPECTED); ChkIfA(results.addrMapped.IsSameIP_and_Port(addrMapped)==false, E_UNEXPECTED); ChkIfA(results.addrLocal.IsSameIP_and_Port(addrLocal)==false, E_UNEXPECTED); Cleanup: return hr; }
HRESULT CBehaviorTest::ProcessResponse(CRefCountedBuffer& spMsg, CSocketAddress& addrRemote, CSocketAddress& addrLocal) { HRESULT hr = S_OK; CStunMessageReader reader; CSocketAddress addrMapped; Chk(BasicReaderValidation(spMsg, reader)); hr = reader.GetXorMappedAddress(&addrMapped); if (FAILED(hr)) { hr = reader.GetMappedAddress(&addrMapped); } Chk(hr); // again drop the message if we can't parse the binding response _fCompleted = true; if (_fIsTest3) { _pResults->addrMappingAA = addrMapped; _pResults->fBehaviorTestSuccess = true; if (addrMapped.IsSameIP_and_Port(_pResults->addrMappingAP)) { _pResults->behavior = ::AddressDependentMapping; } else { _pResults->behavior = ::AddressAndPortDependentMapping; } } else { _pResults->addrMappingAP = addrMapped; if (addrMapped.IsSameIP_and_Port(_pResults->addrMapped)) { _pResults->fBehaviorTestSuccess = true; _pResults->behavior = ::EndpointIndependentMapping; } } Cleanup: return hr; }
HRESULT CTestMessageHandler::ValidateOtherAddress(CStunMessageReader& reader, const CSocketAddress& addrExpected) { HRESULT hr = S_OK; CSocketAddress addr; ChkA(reader.GetOtherAddress(&addr)); ChkIfA(false == addrExpected.IsSameIP_and_Port(addr), E_FAIL); Cleanup: return hr; }
SocketRole CTestClientLogic::GetSocketRoleForDestinationAddress(const CSocketAddress& addrDest) { if (addrDest.IsSameIP_and_Port(_addrServerPP)) { return RolePP; } else if (addrDest.IsSameIP_and_Port(_addrServerPA)) { return RolePA; } else if (addrDest.IsSameIP_and_Port(_addrServerAP)) { return RoleAP; } else if (addrDest.IsSameIP_and_Port(_addrServerAA)) { return RoleAA; } ASSERT(false); return RolePP; }
HRESULT CBasicBindingTest::ProcessResponse(CRefCountedBuffer& spMsg, CSocketAddress& addrRemote, CSocketAddress& addrLocal) { HRESULT hr = S_OK; CStunMessageReader reader; CSocketAddress addrMapped; CSocketAddress addrOther; bool fHasOtherAddress = false; // todo - figure out a way to make buffering TCP fragments work Chk(BasicReaderValidation(spMsg, reader)); hr = reader.GetXorMappedAddress(&addrMapped); if (FAILED(hr)) { hr = reader.GetMappedAddress(&addrMapped); } Chk(hr); // again drop the message if we can't parse the binding response fHasOtherAddress = SUCCEEDED(reader.GetOtherAddress(&addrOther)); // ok, we got a response. So we are done _fCompleted = true; _pResults->fBindingTestSuccess = true; _pResults->fIsDirect = addrLocal.IsSameIP_and_Port(addrMapped); _pResults->addrLocal = addrLocal; _pResults->addrMapped = addrMapped; _pResults->fHasOtherAddress = fHasOtherAddress; if (fHasOtherAddress) { _pResults->addrAA = addrOther; _pResults->addrPA = _pConfig->addrServer; _pResults->addrPA.SetPort(addrOther.GetPort()); _pResults->addrAP = addrOther; _pResults->addrAP.SetPort(_pConfig->addrServer.GetPort()); if (Logging::GetLogLevel() >= LL_DEBUG) { char sz[100]; addrOther.ToStringBuffer(sz, 100); Logging::LogMsg(LL_DEBUG, "Other address is %s\n",sz); } } Cleanup: return hr; }
HRESULT CTestClientLogic::TestBehaviorAndFiltering(bool fBehaviorTest, NatBehavior behavior, bool fFilteringTest, NatFiltering filtering) { HRESULT hr = S_OK; StunClientLogicConfig config; HRESULT hrRet; uint32_t time = 0; CRefCountedBuffer spMsgOut(new CBuffer(MAX_STUN_MESSAGE_SIZE)); CRefCountedBuffer spMsgResponse(new CBuffer(MAX_STUN_MESSAGE_SIZE)); SocketRole outputRole; CSocketAddress addrDummy; StunMessageIn stunmsgIn; StunMessageOut stunmsgOut; CSocketAddress addrDest; CSocketAddress addrMapped; CSocketAddress addrServerResponse; // what address the fake server responded back on StunClientResults results; StunTransactionId transid= {}; //std::string strAddr; ChkA(CommonInit(behavior, filtering)); config.addrServer = _addrServerPP; config.fBehaviorTest = fBehaviorTest; config.fFilteringTest = fFilteringTest; config.timeoutSeconds = 5; config.uMaxAttempts = 10; ChkA(_spClientLogic->Initialize(config)); while (true) { CStunMessageReader reader; bool fDropMessage = false; time += 1000; hrRet = _spClientLogic->GetNextMessage(spMsgOut, &addrDest, time); if (hrRet == E_STUNCLIENT_STILL_WAITING) { //printf("GetNextMessage returned 'still waiting'\n"); continue; } if (hrRet == E_STUNCLIENT_RESULTS_READY) { //printf("GetNextMessage returned 'results ready'\n"); break; } //addrDest.ToString(&strAddr); //printf("Client is sending stun packet to %s\n", strAddr.c_str()); ChkA(GetMappedAddressForDestinationAddress(addrDest, &addrMapped)); //addrMapped.ToString(&strAddr); //printf("Server is receiving stun packet from %s\n", strAddr.c_str()); ChkA(ValidateBindingRequest(spMsgOut, &transid)); // -------------------------------------------------- reader.AddBytes(spMsgOut->GetData(), spMsgOut->GetSize()); ChkIfA(reader.GetState() != CStunMessageReader::BodyValidated, E_UNEXPECTED); // Simulate sending the binding request and getting a response back stunmsgIn.socketrole = GetSocketRoleForDestinationAddress(addrDest); stunmsgIn.addrLocal = addrDest; stunmsgIn.addrRemote = addrMapped; stunmsgIn.fConnectionOriented = false; stunmsgIn.pReader = &reader; stunmsgOut.socketrole = (SocketRole)-1; // intentionally setting it wrong stunmsgOut.addrDest = addrDummy; // we don't care what address the server sent back to stunmsgOut.spBufferOut = spMsgResponse; spMsgResponse->SetSize(0); ChkA(::CStunRequestHandler::ProcessRequest(stunmsgIn, stunmsgOut, &_tsa, NULL)); // simulate the message coming back // make sure we got something! outputRole = stunmsgOut.socketrole; ChkIfA(::IsValidSocketRole(outputRole)==false, E_FAIL); ChkIfA(spMsgResponse->GetSize() == 0, E_FAIL); addrServerResponse = _tsa.set[stunmsgOut.socketrole].addr; // -------------------------------------------------- //addrServerResponse.ToString(&strAddr); //printf("Server is sending back from %s\n", strAddr.c_str()); // if the request went to PP, but came back from AA or AP, then it's likely a filtering test // decide if we need to drop the response fDropMessage = ( addrDest.IsSameIP_and_Port(_addrServerPP) && ( ((outputRole == RoleAA) && (_fAllowChangeRequestAA==false)) || ((outputRole == RolePA) && (_fAllowChangeRequestPA==false)) ) ); //{ // CStunMessageReader::ReaderParseState state; // CStunMessageReader readerDebug; // state = readerDebug.AddBytes(spMsgResponse->GetData(), spMsgResponse->GetSize()); // if (state != CStunMessageReader::BodyValidated) // { // printf("Error - response from server doesn't look valid"); // } // else // { // CSocketAddress addr; // readerDebug.GetMappedAddress(&addr); // addr.ToString(&strAddr); // printf("Response from server indicates our mapped address is %s\n", strAddr.c_str()); // } //} if (fDropMessage == false) { ChkA(_spClientLogic->ProcessResponse(spMsgResponse, addrServerResponse, _addrLocal)); } } // now validate the results results.Init(); // zero it out _spClientLogic->GetResults(&results); ChkIfA(results.behavior != behavior, E_UNEXPECTED); Cleanup: return hr; }
// Test2 - send a binding request to a duplex server instructing it to send back on it's alternate port and alternate IP to an alternate client port HRESULT CTestMessageHandler::Test2() { HRESULT hr = S_OK; CStunMessageBuilder builder; CRefCountedBuffer spBuffer, spBufferOut(new CBuffer(MAX_STUN_MESSAGE_SIZE)); CStunMessageReader reader; StunMessageIn msgIn; StunMessageOut msgOut; TransportAddressSet tas = {}; uint16_t responsePort = 2222; StunChangeRequestAttribute changereq; CStunMessageReader::ReaderParseState state; CSocketAddress addrDestExpected; InitTransportAddressSet(tas, true, true, true, true); InitBindingRequest(builder); builder.AddResponsePort(responsePort); changereq.fChangeIP = true; changereq.fChangePort = true; builder.AddChangeRequest(changereq); builder.AddResponsePort(responsePort); builder.GetResult(&spBuffer); ChkIfA(CStunMessageReader::BodyValidated != reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()), E_FAIL); msgIn.fConnectionOriented = false; msgIn.addrLocal = _addrServerPP; msgIn.pReader = &reader; msgIn.socketrole = RolePP; msgIn.addrRemote = _addrMapped; msgOut.socketrole = RolePP; // deliberate initialized wrong msgOut.spBufferOut = spBufferOut; ChkA(CStunRequestHandler::ProcessRequest(msgIn, msgOut, &tas, NULL)); // parse the response reader.Reset(); state = reader.AddBytes(spBufferOut->GetData(), spBufferOut->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); // validate that the message was sent back from the AA ChkIfA(msgOut.socketrole != RoleAA, E_FAIL); // validate that the server though it was sending back from the AA ChkA(ValidateResponseOriginAddress(reader, _addrServerAA)); // validate that the message was sent to the response port requested addrDestExpected = _addrMapped; addrDestExpected.SetPort(responsePort); ChkIfA(addrDestExpected.IsSameIP_and_Port(msgOut.addrDest)==false, E_FAIL); // validate that the binding response came back ChkA(ValidateMappedAddress(reader, _addrMapped, false)); // the "other" address is still AA (See RFC 3489 - section 8.1) ChkA(ValidateOtherAddress(reader, _addrServerAA)); Cleanup: return hr; }