HRESULT UdpClientLoop(StunClientLogicConfig& config, const ClientSocketConfig& socketconfig) { HRESULT hr = S_OK; CRefCountedStunSocket spStunSocket; CStunSocket stunSocket;; CRefCountedBuffer spMsg(new CBuffer(MAX_STUN_MESSAGE_SIZE)); int sock = -1; CSocketAddress addrDest; // who we send to CSocketAddress addrRemote; // who we CSocketAddress addrLocal; int ret; fd_set set; timeval tv = {}; std::string strAddr; std::string strAddrLocal; StunClientResults results; CStunClientLogic clientlogic; hr = clientlogic.Initialize(config); if (FAILED(hr)) { Logging::LogMsg(LL_ALWAYS, "Unable to initialize client: (error = x%x)", hr); Chk(hr); } hr = stunSocket.UDPInit(socketconfig.addrLocal, RolePP); if (FAILED(hr)) { Logging::LogMsg(LL_ALWAYS, "Unable to create local socket: (error = x%x)", hr); Chk(hr); } stunSocket.EnablePktInfoOption(true); sock = stunSocket.GetSocketHandle(); // let's get a loop going! while (true) { HRESULT hrRet; spMsg->SetSize(0); hrRet = clientlogic.GetNextMessage(spMsg, &addrDest, GetMillisecondCounter()); if (SUCCEEDED(hrRet)) { addrDest.ToString(&strAddr); ASSERT(spMsg->GetSize() > 0); if (Logging::GetLogLevel() >= LL_DEBUG) { std::string strAddr; addrDest.ToString(&strAddr); Logging::LogMsg(LL_DEBUG, "Sending message to %s", strAddr.c_str()); } ret = ::sendto(sock, spMsg->GetData(), spMsg->GetSize(), 0, addrDest.GetSockAddr(), addrDest.GetSockAddrLength()); if (ret <= 0) { Logging::LogMsg(LL_DEBUG, "ERROR. sendto failed (errno = %d)", errno); } // there's not much we can do if "sendto" fails except time out and try again } else if (hrRet == E_STUNCLIENT_STILL_WAITING) { Logging::LogMsg(LL_DEBUG, "Continuing to wait for response..."); } else if (hrRet == E_STUNCLIENT_RESULTS_READY) { break; } else { Logging::LogMsg(LL_DEBUG, "Fatal error (hr == %x)", hrRet); Chk(hrRet); } // now wait for a response spMsg->SetSize(0); FD_ZERO(&set); FD_SET(sock, &set); tv.tv_usec = 500000; // half-second tv.tv_sec = config.timeoutSeconds; ret = select(sock+1, &set, NULL, NULL, &tv); if (ret > 0) { ret = ::recvfromex(sock, spMsg->GetData(), spMsg->GetAllocatedSize(), MSG_DONTWAIT, &addrRemote, &addrLocal); if (ret > 0) { addrRemote.ToString(&strAddr); addrLocal.ToString(&strAddrLocal); Logging::LogMsg(LL_DEBUG, "Got response (%d bytes) from %s on interface %s", ret, strAddr.c_str(), strAddrLocal.c_str()); spMsg->SetSize(ret); clientlogic.ProcessResponse(spMsg, addrRemote, addrLocal); } } } results.Init(); clientlogic.GetResults(&results); DumpResults(config, results); 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; }
void TcpClientLoop(StunClientLogicConfig& config, ClientSocketConfig& socketconfig) { HRESULT hr = S_OK; CStunSocket stunsocket; CStunClientLogic clientlogic; int sock; CRefCountedBuffer spMsg(new CBuffer(1500)); CRefCountedBuffer spMsgReader(new CBuffer(1500)); CSocketAddress addrDest, addrLocal; HRESULT hrRet, hrResult; int ret; size_t bytes_sent, bytes_recv; size_t bytes_to_send, max_bytes_recv, remaining; uint8_t* pData=NULL; size_t readsize; CStunMessageReader reader; StunClientResults results; hr= clientlogic.Initialize(config); if (FAILED(hr)) { Logging::LogMsg(LL_ALWAYS, "clientlogic.Initialize failed (hr == %x)", hr); Chk(hr); } while (true) { stunsocket.Close(); hr = stunsocket.TCPInit(socketconfig.addrLocal, RolePP, true); if (FAILED(hr)) { Logging::LogMsg(LL_ALWAYS, "Unable to create local socket for TCP connection (hr == %x)", hr); Chk(hr); } hrRet = clientlogic.GetNextMessage(spMsg, &addrDest, ::GetMillisecondCounter()); if (hrRet == E_STUNCLIENT_RESULTS_READY) { // clean exit break; } // we should never get a "still waiting" return with TCP, because config.timeout is 0 ASSERT(hrRet != E_STUNCLIENT_STILL_WAITING); if (FAILED(hrRet)) { Chk(hrRet); } // connect to server sock = stunsocket.GetSocketHandle(); ret = ::connect(sock, addrDest.GetSockAddr(), addrDest.GetSockAddrLength()); if (ret == -1) { hrResult = ERRNOHR; Logging::LogMsg(LL_ALWAYS, "Can't connect to server (hr == %x)", hrResult); Chk(hrResult); } Logging::LogMsg(LL_DEBUG, "Connected to server"); bytes_to_send = (int)(spMsg->GetSize()); bytes_sent = 0; pData = spMsg->GetData(); while (bytes_sent < bytes_to_send) { ret = ::send(sock, pData+bytes_sent, bytes_to_send-bytes_sent, 0); if (ret < 0) { hrResult = ERRNOHR; Logging::LogMsg(LL_ALWAYS, "Send failed (hr == %x)", hrResult); Chk(hrResult); } bytes_sent += ret; } Logging::LogMsg(LL_DEBUG, "Request sent - waiting for response"); // consume the response reader.Reset(); reader.GetStream().Attach(spMsgReader, true); pData = spMsg->GetData(); bytes_recv = 0; max_bytes_recv = spMsg->GetAllocatedSize(); remaining = max_bytes_recv; while (remaining > 0) { readsize = reader.HowManyBytesNeeded(); if (readsize == 0) { break; } if (readsize > remaining) { // technically an error, but the client logic will figure it out ASSERT(false); break; } ret = ::recv(sock, pData+bytes_recv, readsize, 0); if (ret == 0) { // server cut us off before we got all the bytes we thought we were supposed to get? ASSERT(false); break; } if (ret < 0) { hrResult = ERRNOHR; Logging::LogMsg(LL_ALWAYS, "Recv failed (hr == %x)", hrResult); Chk(hrResult); } reader.AddBytes(pData+bytes_recv, ret); bytes_recv += ret; remaining = max_bytes_recv - bytes_recv; spMsg->SetSize(bytes_recv); } // now feed the response into the client logic stunsocket.UpdateAddresses(); addrLocal = stunsocket.GetLocalAddress(); clientlogic.ProcessResponse(spMsg, addrDest, addrLocal); } stunsocket.Close(); results.Init(); clientlogic.GetResults(&results); ::DumpResults(config, results); Cleanup: return; }