/** * Suggests a default adapter for a given stun server socket * @param fPrimary - true if the returned adapter is to be used for the primary socket in a stun server. Typically passing "true" means "return the first adapter enumerated", otherwise return the second adapter enumerated" * @param family - Either AF_INET or AF_INET6 * @param pSocketAddr - OUT param. On success, contains the address to bind to * @return S_OK on success. Error code otherwise. */ HRESULT GetBestAddressForSocketBind(bool fPrimary, int family, uint16_t port, CSocketAddress* pSocketAddr) { HRESULT hr = S_OK; ifaddrs* pList = NULL; ifaddrs* pAdapter = NULL; ifaddrs* pAdapter1 = NULL; ifaddrs* pAdapter2 = NULL; ChkIfA(pSocketAddr == NULL, E_INVALIDARG); ChkIf(getifaddrs(&pList) < 0, ERRNOHR); GetDefaultAdapters(family, pList, &pAdapter1, &pAdapter2); pAdapter = fPrimary ? pAdapter1 : pAdapter2; ChkIf(pAdapter==NULL, E_FAIL); ChkIfA(pAdapter->ifa_addr==NULL, E_UNEXPECTED); *pSocketAddr = CSocketAddress(*pAdapter->ifa_addr); pSocketAddr->SetPort(port); Cleanup: freeifaddrs(pList); 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; }
size_t CSocketAddress::GetIPImpl(void* pAddr, size_t length, bool fNBO) const { HRESULT hr = S_OK; size_t bytescopied = 0; ChkIfA(pAddr == NULL, E_INVALIDARG); ChkIfA(length <= 0, E_INVALIDARG); ChkIfA(length < GetIPLength(), E_INVALIDARG); ASSERT((_address.addr.sa_family == AF_INET)||(_address.addr.sa_family == AF_INET6)); if (_address.addr.sa_family == AF_INET) { uint32_t ip = _address.addr4.sin_addr.s_addr; if (fNBO==false) { ip = htonl(ip); } memcpy(pAddr, &ip, sizeof(ip)); bytescopied = sizeof(ip); ASSERT(sizeof(ip) == STUN_IPV4_LENGTH); } else { // ipv6 addresses are the same in either byte ordering - just an array of 16 bytes ASSERT(sizeof(_address.addr6.sin6_addr.s6_addr) == STUN_IPV6_LENGTH); memcpy(pAddr, &_address.addr6.sin6_addr.s6_addr, sizeof(_address.addr6.sin6_addr.s6_addr)); bytescopied = STUN_IPV6_LENGTH; } Cleanup: return bytescopied; }
// 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; }
void CStunThreadMessageHandler::SendResponse() { HRESULT hr = S_OK; ChkIfA(_spStunResponder == NULL, E_FAIL); ChkIfA(_spResponseBuffer->GetSize() <= 0, E_FAIL); Chk(_spStunResponder->SendResponse(_socketOutput, _addrResponse, _spResponseBuffer)); Cleanup: return; }
// test long-credential authentication HRESULT CTestMessageHandler::Test4() { HRESULT hr=S_OK; CStunMessageBuilder builder1, builder2; CStunMessageReader readerResponse; CSocketAddress addrMapped; uint16_t errorcode = 0; char szNonce[MAX_STUN_AUTH_STRING_SIZE+1]; char szRealm[MAX_STUN_AUTH_STRING_SIZE+1]; // ----------------------------------------------------------------------- // simulate a user making a request with no message integrity attribute (or username, or realm) InitBindingRequest(builder1); builder1.FixLengthField(); ChkA(SendHelper(builder1, &readerResponse, _spAuthLong)); Chk(readerResponse.GetErrorCode(&errorcode)); ChkIfA(readerResponse.GetMessageClass() != ::StunMsgClassFailureResponse, E_UNEXPECTED); ChkIf(errorcode != ::STUN_ERROR_UNAUTHORIZED, E_UNEXPECTED); readerResponse.GetStringAttributeByType(STUN_ATTRIBUTE_REALM, szRealm, ARRAYSIZE(szRealm)); readerResponse.GetStringAttributeByType(STUN_ATTRIBUTE_NONCE, szNonce, ARRAYSIZE(szNonce)); // -------------------------------------------------------------------------------- // now simulate the follow-up request readerResponse.Reset(); InitBindingRequest(builder2); builder2.AddNonce(szNonce); builder2.AddRealm(szRealm); builder2.AddUserName("AuthorizedUser"); builder2.AddMessageIntegrityLongTerm("AuthorizedUser", szRealm, "password"); builder2.FixLengthField(); ChkA(SendHelper(builder2, &readerResponse, _spAuthLong)); ChkIfA(readerResponse.GetMessageClass() != ::StunMsgClassSuccessResponse, E_UNEXPECTED); // should have a mapped address ChkA(readerResponse.GetMappedAddress(&addrMapped)); // and the message integrity field should be valid ChkA(readerResponse.ValidateMessageIntegrityLong("AuthorizedUser", szRealm, "password")); Cleanup: return hr; }
HRESULT CTestPolling::WritePipe(PipePair* pPair) { HRESULT hr = S_OK; char ch = 'x'; int ret = -1; ret = write(pPair->writepipe, &ch, 1); ChkIfA(ret < 0, ERRNOHR); ChkIfA(ret == 0, E_UNEXPECTED); pPair->fDataPending = true; Cleanup: return hr; }
// test simple authentication HRESULT CTestMessageHandler::Test3() { CStunMessageBuilder builder1, builder2, builder3; CStunMessageReader readerResponse; uint16_t errorcode = 0; HRESULT hr = S_OK; // ----------------------------------------------------------------------- // simulate an authorized user making a request with a valid password ChkA(InitBindingRequest(builder1)); builder1.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "AuthorizedUser"); builder1.AddMessageIntegrityShortTerm("password"); builder1.FixLengthField(); ChkA(SendHelper(builder1, &readerResponse, _spAuthShort)); ChkA(readerResponse.ValidateMessageIntegrityShort("password")); // ----------------------------------------------------------------------- // simulate a user with a bad password readerResponse.Reset(); InitBindingRequest(builder2); builder2.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "WrongUser"); builder2.AddMessageIntegrityShortTerm("wrongpassword"); builder2.FixLengthField(); ChkA(SendHelper(builder2, &readerResponse, _spAuthShort)) errorcode = 0; ChkA(readerResponse.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 readerResponse.Reset(); ChkA(InitBindingRequest(builder3)); ChkA(SendHelper(builder3, &readerResponse, _spAuthShort)); errorcode = 0; ChkA(readerResponse.GetErrorCode(&errorcode)); ChkIfA(errorcode != ::STUN_ERROR_BADREQUEST, E_FAIL); Cleanup: return hr; }
HRESULT CStunClientTestBase::Init(StunClientLogicConfig* pConfig, StunClientResults* pResults) { HRESULT hr = S_OK; ChkIfA(pConfig == NULL, E_INVALIDARG); ChkIfA(pResults == NULL, E_INVALIDARG); _fInit = true; _pConfig = pConfig; _pResults = pResults; _fCompleted = false; memset(&_transid, 0, sizeof(_transid)); Cleanup: return hr; }
// 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 CTCPStunThread::CreateListenSockets() { HRESULT hr = S_OK; int ret; for (int r = (int)RolePP; r <= (int)RoleAA; r++) { if (_tsaListen.set[r].fValid) { ChkA(_socketListenArray[r].TCPInit(_tsaListen.set[r].addr, (SocketRole)r, true)); _socketTable[r] = _socketListenArray[r].GetSocketHandle(); ChkA(_socketListenArray[r].SetNonBlocking(true)); ret = listen(_socketTable[r], 128); // 128 - large backlog. ChkIfA(ret == -1, ERRNOHR); _countSocks++; } else { _socketTable[r] = -1; } } _fListenSocketsOnEpoll = false; Cleanup: return hr; }
HRESULT CTestPolling::ConsumeEvent(int* pFD, int* pCount) { HRESULT hr = S_OK; HRESULT hrResult = S_OK; PollEvent event; char ch; int result; int count = 0; int fd = -1; int pipesindex = -1; hrResult = _spPolling->WaitForNextEvent(&event, 0); ChkA(hrResult); ChkIfA(hrResult == S_FALSE, S_FALSE); fd = event.fd; while (true) { result = ::read(fd, &ch, 1); if (result <= 0) { break; } count++; } pipesindex = FindPipePairIndex(fd); ChkIfA(pipesindex == -1, E_UNEXPECTED); ChkIfA(count == 0, E_UNEXPECTED); ChkIfA(_pipes[pipesindex].fDataPending == false, E_UNEXPECTED); _pipes[pipesindex].fDataPending = false; Cleanup: if (pFD) { *pFD = fd; } if (pCount) { *pCount = count; } return hr; }
HRESULT CTCPServer::Initialize(const CStunServerConfig& config) { HRESULT hr = S_OK; TransportAddressSet tsaListen; TransportAddressSet tsaHandler; ChkIfA(_threads[0] != NULL, E_UNEXPECTED); // we can't already be initialized, right? // optional code: create an authentication provider and initialize it here (if you want authentication) // set the _spAuth member to reference it // Chk(CYourAuthProvider::CreateInstanceNoInit(&_spAuth)); // tsaHandler is sort of a hack for TCP. It's really just a glorified indication to the the // CStunRequestHandler code to figure out if can offer a CHANGED-ADDRESS attribute. tsaHandler.set[RolePP].fValid = config.fHasPP; tsaHandler.set[RolePP].addr = config.addrPP; tsaHandler.set[RolePA].fValid = config.fHasPA; tsaHandler.set[RolePA].addr = config.addrPA; tsaHandler.set[RoleAP].fValid = config.fHasAP; tsaHandler.set[RoleAP].addr = config.addrAP; tsaHandler.set[RoleAA].fValid = config.fHasAA; tsaHandler.set[RoleAA].addr = config.addrAA; if (config.fMultiThreadedMode == false) { tsaListen = tsaHandler; _threads[0] = new CTCPStunThread(); ChkA(_threads[0]->Init(tsaListen, tsaHandler, _spAuth, config.nMaxConnections)); } else { for (int threadindex = 0; threadindex < 4; threadindex++) { memset(&tsaListen, '\0', sizeof(tsaListen)); if (tsaHandler.set[threadindex].fValid) { tsaListen.set[threadindex] = tsaHandler.set[threadindex]; _threads[threadindex] = new CTCPStunThread(); Chk(_threads[threadindex]->Init(tsaListen, tsaHandler, _spAuth, config.nMaxConnections)); } } } Cleanup: if (FAILED(hr)) { Shutdown(); } return hr; }
HRESULT CStunClientLogic::GetResults(StunClientResults* pResults) { HRESULT hr=S_OK; ChkIfA(pResults == NULL, E_INVALIDARG); *pResults = _results; Cleanup: return hr; }
HRESULT CStunMessageBuilder::AddMessageIntegrityLongTerm(const char* pszUserName, const char* pszRealm, const char* pszPassword) { HRESULT hr = S_OK; const size_t MAX_KEY_SIZE = MAX_STUN_AUTH_STRING_SIZE*3 + 2; uint8_t key[MAX_KEY_SIZE + 1]; // long enough for 64-char strings and two semicolons and a null char for debugging uint8_t hash[MD5_DIGEST_LENGTH] = {}; uint8_t* pResult = NULL; uint8_t* pDst = key; size_t lenUserName = pszUserName ? strlen(pszUserName) : 0; size_t lenRealm = pszRealm ? strlen(pszRealm) : 0; size_t lenPassword = pszPassword ? strlen(pszPassword) : 0; size_t lenTotal = lenUserName + lenRealm + lenPassword + 2; // +2 for the two colons UNREFERENCED_VARIABLE(pResult); ChkIfA(lenTotal > MAX_KEY_SIZE, E_INVALIDARG); // if we ever hit this limit, just increase MAX_STUN_AUTH_STRING_SIZE // too bad CDatastream really only works on refcounted buffers. Otherwise, we wouldn't have to do all this messed up pointer math // We could create a refcounted buffer in this function, but that would mean a call to "new and delete", and we're trying to avoid memory allocations in // critical code paths because they are a proven perf hit // TODO - Fix CDataStream and CBuffer so that "ref counted buffers" are no longer needed pDst = key; memcpy(pDst, pszUserName, lenUserName); pDst += lenUserName; *pDst = ':'; pDst++; memcpy(pDst, pszRealm, lenRealm); pDst += lenRealm; *pDst = ':'; pDst++; memcpy(pDst, pszPassword, lenPassword); pDst += lenPassword; *pDst ='\0'; // null terminate for debugging (this char doesn not get hashed ASSERT((pDst-key) == lenTotal); #ifndef __APPLE__ pResult = MD5(key, lenTotal, hash); #else pResult = CC_MD5(key, lenTotal, hash); #endif ASSERT(pResult != NULL); hr= AddMessageIntegrityImpl(hash, MD5_DIGEST_LENGTH); 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 CTCPStunThread::Start() { int ret; HRESULT hr = S_OK; ChkIfA(_fThreadIsValid, E_FAIL); ChkIf(_pipe[0] == -1, E_UNEXPECTED); // Init hasn't been called _fNeedToExit = false; ret = ::pthread_create(&_pthread, NULL, ThreadFunction, this); ChkIfA(ret != 0, ERRNO_TO_HRESULT(ret)); _fThreadIsValid = true; 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; }
HRESULT CSocketAddress::ToStringBuffer(char* pszAddrBytes, size_t length) const { HRESULT hr = S_OK; int family = GetFamily(); const void *pAddrBytes = NULL; const char* pszResult = NULL; const size_t portLength = 6; // colon plus 5 digit string e.g. ":55555" char szPort[portLength+1]; ChkIfA(pszAddrBytes == NULL, E_INVALIDARG); ChkIf(length <= 0, E_INVALIDARG); pszAddrBytes[0] = 0; if (family == AF_INET) { pAddrBytes = &(_address.addr4.sin_addr); ChkIf(length < (INET_ADDRSTRLEN+portLength), E_INVALIDARG); } else if (family == AF_INET6) { pAddrBytes = &(_address.addr6.sin6_addr); ChkIf(length < (INET6_ADDRSTRLEN+portLength), E_INVALIDARG); } else { ChkA(E_FAIL); } pszResult = ::inet_ntop(family, pAddrBytes, pszAddrBytes, length); ChkIf(pszResult == NULL, ERRNOHR); sprintf(szPort, ":%d", GetPort()); #if DEBUG ChkIfA(strlen(szPort) > portLength, E_FAIL); #endif strcat(pszAddrBytes, szPort); 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; }
HRESULT CTestPolling::SetNonBlocking(int fd) { HRESULT hr = S_OK; int result; int flags; flags = ::fcntl(fd, F_GETFL, 0); ChkIfA(flags == -1, ERRNOHR); flags |= O_NONBLOCK; result = fcntl(fd , F_SETFL , flags); ChkIfA(result == -1, ERRNOHR); 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 CTestPolling::RemovePipe(int pipeindex) { HRESULT hr = S_OK; size_t size = _pipes.size(); ChkIfA(pipeindex < 0, E_FAIL); ChkIfA(pipeindex >= (int)size, E_FAIL); ChkA(_spPolling->Remove(_pipes[pipeindex].readpipe)); close(_pipes[pipeindex].readpipe); _pipes[pipeindex].readpipe = -1; close(_pipes[pipeindex].writepipe); _pipes[pipeindex].writepipe = -1; _pipes.erase(_pipes.begin()+pipeindex); Cleanup: return hr; }
HRESULT CStunMessageBuilder::AddStringAttribute(uint16_t attribType, const char* pstr) { HRESULT hr = S_OK; // I can't think of a single string attribute that could be legitimately empty // AddNonce, AddRealm, AddUserName below depend on this check. So if this check gets removed, add it back to everywhere else ChkIfA(StringHelper::IsNullOrEmpty(pstr), E_INVALIDARG); // AddAttribute allows empty attribute values, so if someone needs to add empty attribute values, do it with that call hr = AddAttribute(attribType, pstr, pstr?strlen(pstr):0); Cleanup: return hr; }
// simplest of all tests. Just set a file descriptor and see that it's available // repeat many times HRESULT CTestPolling::Test1() { HRESULT hr = S_OK; HRESULT hrResult; size_t size; PollEvent event; int fd; int count = 0; srand(100); ChkA(TestInit(10, 10)); size = _pipes.size(); hrResult = _spPolling->WaitForNextEvent(&event, 0); ChkIfA(hrResult != S_FALSE, E_UNEXPECTED); // one event at a time model for (int index = 0; index < 100; index++) { size_t item = rand() % size; ChkA(WritePipe(&_pipes[item])); ConsumeEvent(&fd, &count); ChkIfA(fd != _pipes[item].readpipe, E_UNEXPECTED); ChkIfA(count != 1, E_UNEXPECTED); } hrResult = _spPolling->WaitForNextEvent(&event, 0); ChkIfA(hrResult != S_FALSE, E_UNEXPECTED); Cleanup: return hr; }
HRESULT CTestMessageHandler::ValidateOriginAddress(CStunMessageReader& reader, SocketRole socketExpected) { HRESULT hr = S_OK; StunAttribute attrib; CSocketAddress addrExpected, mappedaddr; CRefCountedBuffer spBuffer; Chk(reader.GetStream().GetBuffer(&spBuffer)); Chk(_spTransport->GetSocketAddressForRole(socketExpected, &addrExpected)); ChkA(reader.GetAttributeByType(STUN_ATTRIBUTE_RESPONSE_ORIGIN, &attrib)); ChkA(GetMappedAddress(spBuffer->GetData()+attrib.offset, attrib.size, &mappedaddr)); ChkIfA(false == addrExpected.IsSameIP_and_Port(mappedaddr), E_FAIL); ChkIfA(socketExpected != _spTransport->m_outputRole, E_FAIL); Cleanup: return hr; }
// Test1 - just do a basic binding request HRESULT CTestMessageHandler::Test1() { HRESULT hr = S_OK; CStunMessageBuilder builder; CRefCountedBuffer spBuffer, spBufferOut(new CBuffer(MAX_STUN_MESSAGE_SIZE)); CStunMessageReader reader; StunMessageIn msgIn; StunMessageOut msgOut; TransportAddressSet tas = {}; InitTransportAddressSet(tas, true, true, true, true); ChkA(InitBindingRequest(builder)); Chk(builder.GetResult(&spBuffer)); ChkIfA(CStunMessageReader::BodyValidated != reader.AddBytes(spBuffer->GetData(), spBuffer->GetSize()), E_FAIL); // a message send to the PP socket on the server from the msgIn.socketrole = RolePP; msgIn.addrRemote = _addrMapped; msgIn.pReader = &reader; msgIn.addrLocal = _addrServerPP; msgIn.fConnectionOriented = false; spBuffer.reset(); msgOut.spBufferOut = spBufferOut; msgOut.socketrole = RoleAA; // deliberately wrong - so we can validate if it got changed to RolePP ChkA(CStunRequestHandler::ProcessRequest(msgIn, msgOut, &tas, NULL)); reader.Reset(); ChkIfA(CStunMessageReader::BodyValidated != reader.AddBytes(spBufferOut->GetData(), spBufferOut->GetSize()), E_FAIL); // validate that the message returned is a success response for a binding request ChkIfA(reader.GetMessageClass() != StunMsgClassSuccessResponse, E_FAIL); ChkIfA(reader.GetMessageType() != (uint16_t)StunMsgTypeBinding, E_FAIL); // Validate that the message came from the server port we expected // and that it's the same address the server set for the origin address ChkIfA(msgOut.socketrole != RolePP, E_FAIL); ChkA(ValidateResponseOriginAddress(reader, _addrServerPP)); ChkIfA(msgOut.addrDest.IsSameIP_and_Port(_addrMapped)==false, E_FAIL); // validate that the mapping was done correctly ChkA(ValidateMappedAddress(reader, _addrMapped, false)); ChkA(ValidateOtherAddress(reader, _addrServerAA)); 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::AddUnknownAttributes(const uint16_t* arr, size_t count) { HRESULT hr = S_OK; uint16_t size = count * sizeof(uint16_t); uint16_t unpaddedsize = size; bool fPad = false; ChkIfA(arr == NULL, E_INVALIDARG); ChkIfA(count <= 0, E_INVALIDARG); // fix for RFC 3489. Since legacy clients can't understand implicit padding rules // of rfc 5389, then we do what rfc 3489 suggests. If there are an odd number of attributes // that would make the length of the attribute not a multiple of 4, then repeat one // attribute. fPad = _fLegacyMode && (!!(count % 2)); if (fPad) { size += sizeof(uint16_t); } Chk(AddAttributeHeader(STUN_ATTRIBUTE_UNKNOWNATTRIBUTES, size)); Chk(_stream.Write(arr, unpaddedsize)); if (fPad) { // repeat the last attribute in the array to get an even alignment of 4 bytes _stream.Write(&arr[count-1], sizeof(arr[0])); } Cleanup: return hr; }