// this test validates that IPV6 addresses work fine with CStunMessageBuilder and CStunMessageReader HRESULT CTestBuilder::Test2() { HRESULT hr = S_OK; CSocketAddress addr(0,0); CSocketAddress addrValidate(0,0); const char* ip6addr = "ABCDEFGHIJKLMNOP"; sockaddr_in6 addr6 = {}; CStunMessageReader reader; StunTransactionId transid; CStunMessageBuilder builder; CRefCountedBuffer spBuffer; addr6.sin6_family = AF_INET6; addr6.sin6_port = htons(9999); memcpy(addr6.sin6_addr.s6_addr, ip6addr, 16); addr = CSocketAddress(addr6); ChkA(builder.AddHeader(StunMsgTypeBinding, StunMsgClassRequest)); ChkA(builder.AddRandomTransactionId(&transid)); ChkA(builder.AddMappedAddress(addr)); ChkA(builder.AddXorMappedAddress(addr)); ChkA(builder.GetResult(&spBuffer)); ChkIfA(CStunMessageReader::BodyValidated != reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()), E_FAIL); ChkA(reader.GetXorMappedAddress(&addrValidate)); ChkIf(addrValidate.IsSameIP_and_Port(addr) == false, E_FAIL); Cleanup: return hr; }
HRESULT CMockTransport::SendResponse(SocketRole roleOutput, const CSocketAddress& addr, CRefCountedBuffer& spResponse) { m_outputRole = roleOutput; m_addrDestination = addr; m_outputstream.Write(spResponse->GetData(), spResponse->GetSize()); return S_OK; }
// This test validates that the construction and parsing of the message integrity attribute in a stun message works as expected // The test also validates both short term and long term credential modes with or without the presence of a fingerprint attribute HRESULT CTestIntegrity::TestMessageIntegrity(bool fWithFingerprint, bool fLongCredentials) { HRESULT hr = S_OK; const char* pszUserName = "******"; const char* pszRealm = "stunrealm"; const char* pszPassword = "******"; CStunMessageBuilder builder; CStunMessageReader reader; uint8_t *pMsg = NULL; size_t sizeMsg = 0; CStunMessageReader::ReaderParseState state; CRefCountedBuffer spBuffer; builder.AddBindingRequestHeader(); builder.AddRandomTransactionId(NULL); builder.AddUserName(pszUserName); builder.AddRealm(pszRealm); if (fLongCredentials == false) { Chk(builder.AddMessageIntegrityShortTerm(pszPassword)); } else { Chk(builder.AddMessageIntegrityLongTerm(pszUserName, pszRealm, pszPassword)); } if (fWithFingerprint) { builder.AddFingerprintAttribute(); } Chk(builder.GetResult(&spBuffer)); pMsg = spBuffer->GetData(); sizeMsg = spBuffer->GetSize(); state = reader.AddBytes(pMsg, sizeMsg); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); ChkIfA(reader.HasMessageIntegrityAttribute()==false, E_FAIL); if (fLongCredentials == false) { ChkA(reader.ValidateMessageIntegrityShort(pszPassword)); } else { ChkA(reader.ValidateMessageIntegrityLong(pszUserName, pszRealm, pszPassword)); } Cleanup: return hr; }
// Test1 - just do a basic binding request HRESULT CTestMessageHandler::Test1() { HRESULT hr=S_OK; CStunMessageBuilder builder; CSocketAddress clientaddr(0x12345678, 9876); CRefCountedBuffer spBuffer; CStunThreadMessageHandler handler; CStunMessageReader reader; CStunMessageReader::ReaderParseState state; StunMessageEnvelope message; _spTransport->Reset(); _spTransport->AddPP(CSocketAddress(0xaaaaaaaa, 1234)); InitBindingRequest(builder); builder.GetStream().GetBuffer(&spBuffer); handler.SetResponder(_spTransport); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->GetOutputStream().GetBuffer(&spBuffer); state = reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); // validate that the binding response matches our expectations ChkA(ValidateMappedAddress(reader, clientaddr)); // validate that it came from the server port we expected ChkA(ValidateOriginAddress(reader, RolePP)); // did we get back the binding request we expected ChkA(ValidateResponseAddress(clientaddr)); Cleanup: return hr; }
HRESULT CStunMessageBuilder::AddMessageIntegrityImpl(uint8_t* key, size_t keysize) { HRESULT hr = S_OK; const size_t c_hmacsize = 20; uint8_t hmacvaluedummy[c_hmacsize] = {}; // zero-init unsigned int resultlength = c_hmacsize; uint8_t* pDstBuf = NULL; CRefCountedBuffer spBuffer; void* pData = NULL; size_t length = 0; unsigned char* pHashResult = NULL; UNREFERENCED_VARIABLE(pHashResult); ChkIfA(key==NULL || keysize <= 0, E_INVALIDARG); // add in a "zero-init" HMAC value. This adds 24 bytes to the length Chk(AddAttribute(STUN_ATTRIBUTE_MESSAGEINTEGRITY, hmacvaluedummy, ARRAYSIZE(hmacvaluedummy))); Chk(FixLengthField()); // now do a SHA1 on everything but the last 24 bytes (4 bytes of the attribute header and 20 bytes for the dummy content) ChkA(_stream.GetBuffer(&spBuffer)); pData = spBuffer->GetData(); length = spBuffer->GetSize(); ASSERT(length > 24); length = length-24; // now do a little pointer math so that HMAC can write exactly to where the hash bytes will appear pDstBuf = ((uint8_t*)pData) + length + 4; #ifndef __APPLE__ pHashResult = HMAC(EVP_sha1(), key, keysize, (uint8_t*)pData, length, pDstBuf, &resultlength); ASSERT(resultlength == 20); ASSERT(pHashResult != NULL); #else CCHmac(kCCHmacAlgSHA1, key, keysize,(uint8_t*)pData, length, pDstBuf); UNREFERENCED_VARIABLE(resultlength); #endif Cleanup: return hr; }
HRESULT CTestClientLogic::ValidateBindingRequest(CRefCountedBuffer& spMsg, StunTransactionId* pTransId) { HRESULT hr = S_OK; CStunMessageReader reader; CStunMessageReader::ReaderParseState state; state = reader.AddBytes(spMsg->GetData(), spMsg->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_UNEXPECTED); ChkIfA(reader.GetMessageType() != StunMsgTypeBinding, E_UNEXPECTED); reader.GetTransactionId(pTransId); ChkIfA(false == IsTransactionIdValid(*pTransId), E_FAIL); Cleanup: return hr; }
void CStunThreadMessageHandler::SendErrorResponse() { HRESULT hr = S_OK; CStunMessageBuilder builder; CRefCountedBuffer spBuffer; _spResponseBuffer->SetSize(0); builder.GetStream().Attach(_spResponseBuffer, true); builder.AddHeader((StunMessageType)_error.msgtype, _error.msgclass); builder.AddTransactionId(_transid); builder.AddErrorCode(_error.errorcode, "FAILED"); if ((_error.errorcode == ::STUN_ERROR_UNKNOWNATTRIB) && (_error.attribUnknown != 0)) { builder.AddUnknownAttributes(&_error.attribUnknown, 1); } else if ((_error.errorcode == ::STUN_ERROR_STALENONCE) || (_error.errorcode == ::STUN_ERROR_UNAUTHORIZED)) { if (_error.szNonce[0]) { builder.AddStringAttribute(STUN_ATTRIBUTE_NONCE, _error.szNonce); } if (_error.szRealm[0]) { builder.AddStringAttribute(STUN_ATTRIBUTE_REALM, _error.szRealm); } } ChkIfA(_spStunResponder == NULL, E_FAIL); builder.GetResult(&spBuffer); ASSERT(spBuffer->GetSize() != 0); ASSERT(spBuffer == _spResponseBuffer); _spStunResponder->SendResponse(_socketOutput, _addrResponse, spBuffer); Cleanup: return; }
HRESULT CStunClientTestBase::BasicReaderValidation(CRefCountedBuffer& spMsg, CStunMessageReader& reader) { HRESULT hr = S_OK; CStunMessageReader::ReaderParseState readerstate; StunTransactionId transid; int cmp = 0; readerstate = reader.AddBytes(spMsg->GetData(), spMsg->GetSize()); ChkIf(readerstate != CStunMessageReader::BodyValidated, E_FAIL); reader.GetTransactionId(&transid); cmp = memcmp(transid.id, _transid.id, sizeof(_transid)); ChkIf(cmp!=0, E_FAIL); Cleanup: return hr; }
HRESULT CStunClientLogic::ProcessResponse(CRefCountedBuffer& spMsg, CSocketAddress& addrRemote, CSocketAddress& addrLocal) { HRESULT hr = S_OK; IStunClientTest* pCurrentTest = NULL; ChkIfA(_fInitialized == false, E_FAIL); ChkIfA(spMsg->GetSize() == 0, E_INVALIDARG); ChkIf (_nTestIndex >= _testlist.size(), E_UNEXPECTED); pCurrentTest = _testlist[_nTestIndex]; // passing a response to a test that is already completed ?? ChkIfA(pCurrentTest->IsCompleted(), E_UNEXPECTED); hr = pCurrentTest->ProcessResponse(spMsg, addrRemote, addrLocal); // this likely puts the test into the completed state // A subsequent call to GetNextMessage will invoke the next tset Cleanup: return hr; }
HRESULT CStunMessageBuilder::AddFingerprintAttribute() { boost::crc_32_type result; uint32_t value; CRefCountedBuffer spBuffer; void* pData = NULL; size_t length = 0; int offset; HRESULT hr = S_OK; Chk(_stream.WriteUint16(htons(STUN_ATTRIBUTE_FINGERPRINT))); Chk(_stream.WriteUint16(htons(sizeof(uint32_t)))); // field length is 4 bytes Chk(_stream.WriteUint32(0)); // dummy value for start Chk(FixLengthField()); // now do a CRC-32 on everything but the last 8 bytes ChkA(_stream.GetBuffer(&spBuffer)); pData = spBuffer->GetData(); length = spBuffer->GetSize(); ASSERT(length > 8); length = length-8; result.process_bytes(pData, length); value = result.checksum(); value = value ^ STUN_FINGERPRINT_XOR; offset = -(int)(sizeof(value)); Chk(_stream.SeekRelative(offset)); Chk(_stream.WriteUint32(htonl(value))); Cleanup: return hr; }
// The goal of this test is to just validate that we can create a message from CStunMessageBuilder and have it's output parsed correctly by CStunMessageReader // Also helps validate CSocketAddress HRESULT CTestBuilder::Test1() { HRESULT hr = S_OK; CStunMessageBuilder builder; CStunMessageReader reader; StunAttribute attrib; CRefCountedBuffer spBuffer; CRefCountedBuffer spBufferReader; CSocketAddress addrValidate(0,0); StunTransactionId transid = {}; uint32_t ipvalidate = 0; CSocketAddress addr(0x7f000001, 9999); CSocketAddress addrOrigin(0xAABBCCDD, 8888); CSocketAddress addrOther(0x11223344, 7777); ChkA(builder.AddBindingRequestHeader()); ChkA(builder.AddRandomTransactionId(&transid)); ChkA(builder.AddStringAttribute(STUN_ATTRIBUTE_SOFTWARE, "FOOBAR")); ChkA(builder.AddMappedAddress(addr)); ChkA(builder.AddXorMappedAddress(addr)); ChkA(builder.AddOtherAddress(addrOther)); ChkA(builder.AddResponseOriginAddress(addrOrigin)); ChkA(builder.AddFingerprintAttribute()); ChkA(builder.GetResult(&spBuffer)); ChkIfA(CStunMessageReader::BodyValidated != reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()), E_FAIL); ChkIfA(reader.HasFingerprintAttribute() == false, E_FAIL); ChkIfA(reader.IsFingerprintAttributeValid() == false, E_FAIL); ChkIfA(reader.GetMessageClass() != StunMsgClassRequest, E_FAIL); ChkIfA(reader.GetMessageType() != StunMsgTypeBinding, E_FAIL); ChkA(reader.GetBuffer(&spBufferReader)); ChkA(reader.GetAttributeByType(STUN_ATTRIBUTE_SOFTWARE, &attrib)); ChkIfA(attrib.attributeType != STUN_ATTRIBUTE_SOFTWARE, E_FAIL); ChkIfA(0 != ::strncmp("FOOBAR", (const char*)(spBufferReader->GetData() + attrib.offset), attrib.size), E_FAIL); ChkA(reader.GetXorMappedAddress(&addrValidate)); ChkIf(addrValidate.IsSameIP_and_Port(addr) == false, E_FAIL); ChkIfA(addrValidate.GetIPLength() != 4, E_FAIL); addrValidate = CSocketAddress(0,0); ChkA(reader.GetMappedAddress(&addrValidate)); ChkIfA(addrValidate.GetPort() != 9999, E_FAIL); ChkIfA(addrValidate.GetIPLength() != 4, E_FAIL); ChkIfA(4 != addrValidate.GetIP(&ipvalidate, 4), E_FAIL); ChkIfA(ipvalidate != 0x7f000001, E_FAIL); addrValidate = CSocketAddress(0,0); ipvalidate = 0; reader.GetOtherAddress(&addrValidate); ChkIfA(addrValidate.GetPort() != 7777, E_FAIL); ChkIfA(addrValidate.GetIPLength() != 4, E_FAIL); ChkIfA(4 != addrValidate.GetIP(&ipvalidate, 4), E_FAIL); ChkIf(ipvalidate != 0x11223344, E_FAIL); Cleanup: return hr; }
// test long-credential authentication HRESULT CTestMessageHandler::Test4() { HRESULT hr=S_OK; CStunMessageBuilder builder1, builder2; CStunMessageReader reader1, reader2; CSocketAddress clientaddr(0x12345678, 9876); CSocketAddress addrMapped; CRefCountedBuffer spBuffer; CStunThreadMessageHandler handler; uint16_t errorcode = 0; char szNonce[MAX_STUN_AUTH_STRING_SIZE+1]; char szRealm[MAX_STUN_AUTH_STRING_SIZE+1]; CStunMessageReader::ReaderParseState state; StunMessageEnvelope message; _spTransport->Reset(); _spTransport->AddPP(CSocketAddress(0xaaaaaaaa, 1234)); handler.SetAuth(_spAuthLong); handler.SetResponder(_spTransport); // ----------------------------------------------------------------------- // simulate a user making a request with no message integrity attribute (or username, or realm) InitBindingRequest(builder1); builder1.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader1.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); // we expect the response back will be a 401 with a provided nonce and realm Chk(reader1.GetErrorCode(&errorcode)); ChkIfA(reader1.GetMessageClass() != ::StunMsgClassFailureResponse, E_UNEXPECTED); ChkIf(errorcode != ::STUN_ERROR_UNAUTHORIZED, E_UNEXPECTED); reader1.GetStringAttributeByType(STUN_ATTRIBUTE_REALM, szRealm, ARRAYSIZE(szRealm)); reader1.GetStringAttributeByType(STUN_ATTRIBUTE_NONCE, szNonce, ARRAYSIZE(szNonce)); // -------------------------------------------------------------------------------- // now simulate the follow-up request _spTransport->ClearStream(); spBuffer.reset(); InitBindingRequest(builder2); builder2.AddNonce(szNonce); builder2.AddRealm(szRealm); builder2.AddUserName("AuthorizedUser"); builder2.AddMessageIntegrityLongTerm("AuthorizedUser", szRealm, "password"); builder2.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader2.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); ChkIfA(reader2.GetMessageClass() != ::StunMsgClassSuccessResponse, E_UNEXPECTED); // should have a mapped address ChkA(reader2.GetMappedAddress(&addrMapped)); // and the message integrity field should be valid ChkA(reader2.ValidateMessageIntegrityLong("AuthorizedUser", szRealm, "password")); Cleanup: return hr; }
// test simple authentication HRESULT CTestMessageHandler::Test3() { HRESULT hr=S_OK; CStunMessageBuilder builder1, builder2, builder3; CStunMessageReader reader1, reader2, reader3; CSocketAddress clientaddr(0x12345678, 9876); CRefCountedBuffer spBuffer; CStunThreadMessageHandler handler; uint16_t errorcode = 0; CStunMessageReader::ReaderParseState state; StunMessageEnvelope message; _spTransport->Reset(); _spTransport->AddPP(CSocketAddress(0xaaaaaaaa, 1234)); handler.SetAuth(_spAuthShort); handler.SetResponder(_spTransport); // ----------------------------------------------------------------------- // simulate an authorized user making a request with a valid password InitBindingRequest(builder1); builder1.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "AuthorizedUser"); builder1.AddMessageIntegrityShortTerm("password"); builder1.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); handler.ProcessRequest(message); // we expect back a response with a valid message integrity field spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader1.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); ChkA(reader1.ValidateMessageIntegrityShort("password")); // ----------------------------------------------------------------------- // simulate a user with a bad password spBuffer.reset(); InitBindingRequest(builder2); builder2.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "WrongUser"); builder2.AddMessageIntegrityShortTerm("wrongpassword"); builder2.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); _spTransport->ClearStream(); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader2.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); errorcode = 0; ChkA(reader2.GetErrorCode(&errorcode)); ChkIfA(errorcode != ::STUN_ERROR_UNAUTHORIZED, E_FAIL); // ----------------------------------------------------------------------- // simulate a client sending no credentials - we expect it to fire back with a 400/bad-request spBuffer.reset(); InitBindingRequest(builder3); builder3.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(message.localSocket, &(message.localAddr)); _spTransport->ClearStream(); handler.ProcessRequest(message); spBuffer.reset(); _spTransport->m_outputstream.GetBuffer(&spBuffer); state = reader3.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); errorcode = 0; ChkA(reader3.GetErrorCode(&errorcode)); ChkIfA(errorcode != ::STUN_ERROR_BADREQUEST, E_FAIL); Cleanup: return hr; }
// 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; CSocketAddress clientaddr(0x12345678, 9876); CSocketAddress recvaddr; uint16_t responsePort = 2222; CRefCountedBuffer spBuffer; CStunThreadMessageHandler handler; CStunMessageReader reader; CStunMessageReader::ReaderParseState state; ::StunChangeRequestAttribute changereq; StunMessageEnvelope message; _spTransport->Reset(); _spTransport->AddPP(CSocketAddress(0xaaaaaaaa, 1234)); _spTransport->AddPA(CSocketAddress(0xaaaaaaaa, 1235)); _spTransport->AddAP(CSocketAddress(0xbbbbbbbb, 1234)); _spTransport->AddAA(CSocketAddress(0xbbbbbbbb, 1235)); InitBindingRequest(builder); builder.AddResponsePort(responsePort); changereq.fChangeIP = true; changereq.fChangePort = true; builder.AddChangeRequest(changereq); builder.AddResponsePort(responsePort); builder.GetResult(&spBuffer); message.localSocket = RolePP; message.remoteAddr = clientaddr; message.spBuffer = spBuffer; _spTransport->GetSocketAddressForRole(RolePP, &(message.localAddr)); handler.SetResponder(_spTransport); handler.ProcessRequest(message); spBuffer->Reset(); _spTransport->GetOutputStream().GetBuffer(&spBuffer); // parse the response state = reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); // validate that the binding response matches our expectations ChkA(ValidateMappedAddress(reader, clientaddr)); ChkA(ValidateOriginAddress(reader, RoleAA)); // did it get sent back to where we thought it was recvaddr = clientaddr; recvaddr.SetPort(responsePort); ChkA(ValidateResponseAddress(recvaddr)); Cleanup: return hr; }
HRESULT CTestMessageHandler::SendHelper(CStunMessageBuilder& builderRequest, CStunMessageReader* pReaderResponse, IStunAuth* pAuth) { CRefCountedBuffer spBufferRequest; CRefCountedBuffer spBufferResponse(new CBuffer(MAX_STUN_MESSAGE_SIZE)); StunMessageIn msgIn; StunMessageOut msgOut; CStunMessageReader reader; CSocketAddress addrDest; TransportAddressSet tas; HRESULT hr = S_OK; InitTransportAddressSet(tas, true, true, true, true); builderRequest.GetResult(&spBufferRequest); ChkIf(CStunMessageReader::BodyValidated != reader.AddBytes(spBufferRequest->GetData(), spBufferRequest->GetSize()), E_FAIL); msgIn.fConnectionOriented = false; msgIn.addrLocal = _addrServerPP; msgIn.pReader = &reader; msgIn.socketrole = RolePP; msgIn.addrRemote = _addrMapped; msgOut.spBufferOut = spBufferResponse; ChkA(CStunRequestHandler::ProcessRequest(msgIn, msgOut, &tas, pAuth)); ChkIf(CStunMessageReader::BodyValidated != pReaderResponse->AddBytes(spBufferResponse->GetData(), spBufferResponse->GetSize()), E_FAIL); Cleanup: return hr; }