HRESULT CStunMessageBuilder::AddPaddingAttribute(uint16_t paddingSize) { HRESULT hr = S_OK; const uint16_t PADDING_BUFFER_SIZE = 128; static char padding_bytes[PADDING_BUFFER_SIZE] = {}; // round up so we're a multiple of 4 if (paddingSize % 4) { paddingSize = paddingSize + 4 - (paddingSize % 4); } Chk(AddAttributeHeader(STUN_ATTRIBUTE_PADDING, paddingSize)); while (paddingSize > 0) { uint16_t blocksize = (paddingSize >= PADDING_BUFFER_SIZE) ? PADDING_BUFFER_SIZE : paddingSize; Chk(_stream.Write(padding_bytes, blocksize)); paddingSize -= blocksize; } Cleanup: return hr; }
HRESULT CStunMessageBuilder::AddMappedAddressImpl(uint16_t attribute, const CSocketAddress& addr) { uint16_t port; size_t length; uint8_t ip[STUN_IPV6_LENGTH]; HRESULT hr = S_OK; uint8_t family = (addr.GetFamily()==AF_INET) ? STUN_ATTRIBUTE_FIELD_IPV4 :STUN_ATTRIBUTE_FIELD_IPV6; size_t attributeSize = (family == STUN_ATTRIBUTE_FIELD_IPV4) ? STUN_ATTRIBUTE_MAPPEDADDRESS_SIZE_IPV4 : STUN_ATTRIBUTE_MAPPEDADDRESS_SIZE_IPV6; Chk(AddAttributeHeader(attribute, attributeSize)); port = addr.GetPort_NBO(); length = addr.GetIP_NBO(ip, sizeof(ip)); // if we ever had a length that was not a multiple of 4, we'd need to add padding ASSERT((length == STUN_IPV4_LENGTH) || (length == STUN_IPV6_LENGTH)); Chk(_stream.WriteUint8(0)); Chk(_stream.WriteUint8(family)); Chk(_stream.WriteUint16(port)); Chk(_stream.Write(ip, length)); Cleanup: return hr; }
int main(int argc, char **argv) { struct tcpe_error* err = NULL; struct tcpe_client* cl = NULL; Chk(tcpe_client_init(&cl)); printf("%-8s %-20s %-8s %-20s %-8s\n", "CID", "LocalAddr", "LocalPort", "RemAddr", "RemPort"); printf("-------- -------------------- -------- -------------------- --------\n"); printf("\n"); Chk(tcpe_list_conns(cl, connection_callback)); Cleanup: tcpe_client_destroy(&cl); if (err != NULL) { PRINT_AND_FREE(err); return EXIT_FAILURE; } return EXIT_SUCCESS; }
HRESULT CTestBuilder::Run() { HRESULT hr = S_OK; Chk(Test1()) Chk(Test2()); Cleanup: return hr; }
// 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; }
HRESULT CStunMessageBuilder::AddAttributeHeader(uint16_t attribType, uint16_t size) { HRESULT hr = S_OK; Chk(_stream.WriteUint16(htons(attribType))); Chk(_stream.WriteUint16(htons(size))); Cleanup: return hr; }
HRESULT CTestRecvFromEx::Run() { HRESULT hr = S_OK; Chk(DoTest(false)); // ipv4 Chk(DoTest(true)); // ipv6 Cleanup: return hr; }
HRESULT CStunMessageBuilder::AddErrorCode(uint16_t errorNumber, const char* pszReason) { HRESULT hr = S_OK; uint8_t padBytes[4] = {0}; size_t strsize = (pszReason==NULL) ? 0 : strlen(pszReason); size_t size = strsize + 4; size_t sizeheader = size; size_t padding = 0; uint8_t cl = 0; uint8_t ernum = 0; ChkIf(strsize >= 128, E_INVALIDARG); ChkIf(errorNumber < 300, E_INVALIDARG); ChkIf(errorNumber > 600, E_INVALIDARG); padding = (size%4) ? (4-size%4) : 0; // fix for RFC 3489 clients - explicitly do the 4-byte padding alignment on the string with spaces instead of // padding the message with zeros. Adjust the length field to always be a multiple of 4. if ((size % 4) && _fLegacyMode) { padding = 4 - (size % 4); } if (_fLegacyMode) { sizeheader += padding; } Chk(AddAttributeHeader(STUN_ATTRIBUTE_ERRORCODE, sizeheader)); Chk(_stream.WriteInt16(0)); cl = (uint8_t)(errorNumber / 100); ernum = (uint8_t)(errorNumber % 100); Chk(_stream.WriteUint8(cl)); Chk(_stream.WriteUint8(ernum)); if (strsize > 0) { _stream.Write(pszReason, strsize); } if (padding > 0) { Chk(_stream.Write(padBytes, padding)); } Cleanup: return hr; }
HRESULT CTestMessageHandler::Run() { HRESULT hr = S_OK; Chk(Test1()); Chk(Test2()); Chk(Test3()); Chk(Test4()); Cleanup: return hr; }
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; }
int main(int argc, char **argv) { estats_error* err = NULL; struct estats_nl_client* cl = NULL; estats_val_data* data = NULL; int cid, i, j; int opt, option; char varname[24]; char *endptr, *str; uintmax_t val; if (argc < 4) { usage(); exit(EXIT_FAILURE); } while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': usage(); exit(EXIT_SUCCESS); break; default: exit(EXIT_FAILURE); break; } } cid = atoi(argv[1]); strncpy(varname, argv[2], 24); str = argv[3]; val = strtoumax(str, &endptr, 10); Chk(estats_nl_client_init(&cl)); Chk(estats_write_var(varname, (uint32_t)val, cid, cl)); Cleanup: estats_nl_client_destroy(&cl); if (err != NULL) { PRINT_AND_FREE(err); return EXIT_FAILURE; } return EXIT_SUCCESS; }
HRESULT CTestIntegrity::Run() { HRESULT hr = S_OK; Chk(TestMessageIntegrity(false, false)); ChkA(TestMessageIntegrity(true, false)); Chk(TestMessageIntegrity(false, true)); ChkA(TestMessageIntegrity(true, true)); Cleanup: return hr; }
HRESULT CFilteringTest::ProcessResponse(CRefCountedBuffer& spMsg, CSocketAddress& addrRemote, CSocketAddress& addrLocal) { HRESULT hr = S_OK; CStunMessageReader reader; Chk(BasicReaderValidation(spMsg, reader)); // BasicReaderValidation will check the transaction ID! // we don't really care what's in the response other than if the transactionID is correct _fCompleted = true; if (_fIsTest3) { _pResults->fGotTest3Response = true; _pResults->fFilteringTestSuccess = true; _pResults->filtering = ::AddressDependentFiltering; } else { // if we got a response back from the other IP and Port, then we have independent filtering _pResults->fGotTest2Response = true; _pResults->fFilteringTestSuccess = true; _pResults->filtering = ::EndpointIndependentFiltering; } Cleanup: return hr; }
int Vector<T>::GetIndex(const T& item) const { Chk(); if(vector == NULL) return -1; int n = &item - vector; return n >= 0 && n < items ? n : -1; }
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 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; }
void Vector<T>::Remove(int q, int count) { Chk(); ASSERT(q >= 0 && q <= items - count && count >= 0); if(count == 0) return; DestroyArray(vector + q, vector + q + count); memmove(vector + q, vector + q + count, (items - q - count) * sizeof(T)); items -= count; }
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 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; }
// 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; }
void Vector<T>::InsertPick(int i, pick_ Vector<T>& v) { Chk(); v.Chk(); ASSERT(!vector || v.vector != vector); if(v.items) { RawInsert(i, v.items); memcpy(vector + i, v.vector, sizeof(T) * v.items); } RawFree(v.vector); SetPicked(v); }
void Vector<T>::SetCount(int n) { Chk(); ASSERT(n >= 0); if(n == items) return; if(n < items) Trim(n); else { if(n > alloc) ReAllocF(n); ConstructArray(vector + items, vector + n); items = n; } }
template <class T> inline void Vector<T>::AddN(int n) { Chk(); ASSERT(n >= 0); if(items + n <= alloc) { ConstructArray(vector + items, vector + items + n); items += n; } else SetCountR(items + n); }
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; }
HRESULT GetMappedAddress(uint8_t* pData, size_t size, CSocketAddress* pAddr) { uint16_t port; HRESULT hr = S_OK; uint8_t attributeid; uint8_t ip6[STUN_IPV6_LENGTH]; uint32_t ip4; CRefCountedBuffer spBuffer(new CBuffer(pData, size, false)); CDataStream stream(spBuffer); ChkIfA(pAddr==NULL, E_INVALIDARG); Chk(stream.SeekDirect(1)); // skip over the zero byte Chk(stream.ReadUint8(&attributeid)); Chk(stream.ReadUint16(&port)); port = ntohs(port); if (attributeid == STUN_ATTRIBUTE_FIELD_IPV4) { Chk(stream.ReadUint32(&ip4)); ip4 = ntohl(ip4); *pAddr = CSocketAddress(ip4, port); } else { sockaddr_in6 addr6={}; Chk(stream.Read(ip6, STUN_IPV6_LENGTH)); addr6.sin6_family = AF_INET6; addr6.sin6_port = htons(port); memcpy(&addr6.sin6_addr, ip6, STUN_IPV6_LENGTH); *pAddr = CSocketAddress(addr6); } Cleanup: return hr; }
HRESULT CTestReader::Test2() { HRESULT hr = S_OK; // this test is to validate an extreme case for TCP scenarios. // what if the bytes only arrived "one at a time"? // or if the byte chunks straddled across logical parse segments (i.e. the header and the body) // Can CStunMessageReader::AddBytes handle and parse out the correct result for (size_t chunksize = 1; chunksize <= 30; chunksize++) { Chk(TestFixedReadSizes(chunksize)); } srand(888); for (size_t i = 0; i < 200; i++) { Chk(TestFixedReadSizes(0)); } Cleanup: return hr; }
HRESULT CStunMessageBuilder::AddAttribute(uint16_t attribType, const void* data, uint16_t size) { uint8_t padBytes[4] = {0}; size_t padding = 0; HRESULT hr = S_OK; uint16_t sizeheader = size; if (data == NULL) { size = 0; } // attributes always start on a 4-byte boundary padding = (size % 4) ? (4 - (size % 4)) : 0; if (_fLegacyMode) { // in legacy mode (RFC 3489), the header size of the attribute includes the padding // in RFC 5389, the attribute header is the exact size of the data, and extra padding bytes are implicitly assumed sizeheader += padding; } // I suppose you can have zero length attributes as an indicator of something Chk(AddAttributeHeader(attribType, sizeheader)); if (size > 0) { Chk(_stream.Write(data, size)); } // pad with zeros to get the 4-byte alignment if (padding > 0) { Chk(_stream.Write(padBytes, padding)); } Cleanup: return hr; }
void Vector<T>::Set(int i, const T& x, int count) { Chk(); ASSERT(i >= 0 && count >= 0); if(count == 0) return; if(&x >= vector && &x < vector + items) { T copy = x; At(i + count - 1); Fill(vector + i, vector + i + count, copy); } else { At(i + count - 1); Fill(vector + i, vector + i + count, x); } }
HRESULT GetXorMappedAddress(uint8_t* pData, size_t size, StunTransactionId &transid, CSocketAddress* pAddr) { HRESULT hr = S_OK; Chk(GetMappedAddress(pData, size, pAddr)); pAddr->ApplyStunXorMap(transid); 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; }