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 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; }
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; }