HRESULT CSocketAddress::GetLocalHost(uint16_t family, CSocketAddress* pAddr) { if ( ((family != AF_INET) && (family != AF_INET6)) || (pAddr == NULL)) { ASSERT(false); return E_FAIL; } if (family == AF_INET) { uint32_t ip = 0x7f000001; // 127.0.0.1 in host byte order *pAddr = CSocketAddress(ip, 0); } else { sockaddr_in6 addr6 = {}; COMPILE_TIME_ASSERT(sizeof(addr6.sin6_addr) == 16); // ::1 uint8_t ip6[16] = {}; ip6[15] = 1; addr6.sin6_family = AF_INET6; memcpy(&addr6.sin6_addr, ip6, 16); *pAddr = CSocketAddress(addr6); } return S_OK; }
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; }
CSocketAddress CGSocket::GetPeerName( ) const { struct sockaddr_in SockAddrIn; int iRet = GetPeerName( &SockAddrIn ); if ( iRet ) { return( CSocketAddress( INADDR_BROADCAST, 0 )); // invalid. } else { return( CSocketAddress( SockAddrIn )); } }
/** * 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; }
// 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; }
void CStunSocket::Close() { if (_sock != -1) { close(_sock); _addrlocal = CSocketAddress(0,0); } }
HRESULT CMockTransport::ClearStream() { m_outputstream.Reset(); m_outputRole = (SocketRole)-1; m_addrDestination = CSocketAddress(); return S_OK; }
HRESULT CMockTransport::Reset() { for (unsigned int index = 0; index < ARRAYSIZE(m_addrs); index++) { m_addrs[index] = CSocketAddress(); } ClearStream(); return S_OK; }
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; }
// 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; }
//static HRESULT CStunSocket::Create(const CSocketAddress& addrlocal, SocketRole role, boost::shared_ptr<CStunSocket>* pStunSocketShared) { int sock = -1; int ret; CStunSocket* pSocket = NULL; sockaddr_storage addrBind = {}; socklen_t sizeaddrBind; HRESULT hr = S_OK; ChkIfA(pStunSocketShared == NULL, E_INVALIDARG); sock = socket(addrlocal.GetFamily(), SOCK_DGRAM, 0); ChkIf(sock < 0, ERRNOHR); ret = bind(sock, addrlocal.GetSockAddr(), addrlocal.GetSockAddrLength()); ChkIf(ret < 0, ERRNOHR); // call get sockname to find out what port we just binded to. (Useful for when addrLocal.port is 0) sizeaddrBind = sizeof(addrBind); ret = ::getsockname(sock, (sockaddr*)&addrBind, &sizeaddrBind); ChkIf(ret < 0, ERRNOHR); pSocket = new CStunSocket(); pSocket->_sock = sock; pSocket->_addrlocal = CSocketAddress(*(sockaddr*)&addrBind); pSocket->_role = role; sock = -1; { boost::shared_ptr<CStunSocket> spTmp(pSocket); pStunSocketShared->swap(spTmp); } Cleanup: if (sock != -1) { close(sock); sock = -1; } return hr; }
void CTCPServer::InitTSA(TransportAddressSet* pTSA, SocketRole role, bool fValid, const CSocketAddress& addrListen, const CSocketAddress& addrAdvertise) { if (fValid == false) { pTSA->set[role].fValid = false; pTSA->set[role].addr = CSocketAddress(); } else { pTSA->set[role].fValid = true; if (addrAdvertise.IsIPAddressZero()) { pTSA->set[role].addr = addrListen; } else { pTSA->set[role].addr = addrAdvertise; pTSA->set[role].addr.SetPort(addrListen.GetPort()); } } }
StunConnection* CTCPStunThread::AcceptConnection(CStunSocket* pListenSocket) { int listensock = pListenSocket->GetSocketHandle(); SocketRole role = pListenSocket->GetRole(); int clientsock = -1; int socktmp = -1; sockaddr_storage addrClient; socklen_t socklen = sizeof(addrClient); StunConnection* pConn = NULL; HRESULT hr = S_OK; int insertresult; int err; ASSERT(listensock != -1); ASSERT(::IsValidSocketRole(role)); socktmp = ::accept(listensock, (sockaddr*)&addrClient, &socklen); err = errno; Logging::LogMsg(LL_VERBOSE, "accept returns %d (errno == %d)", socktmp, (socktmp<0)?err:0); ChkIfA(socktmp == -1, E_FAIL); // --- rate limit check------- if (_spLimiter.get()) { CSocketAddress addr = CSocketAddress(addrClient); bool allowed_to_pass = _spLimiter->RateCheck(addr); if (allowed_to_pass == false) { if (Logging::GetLogLevel() >= LL_VERBOSE) { char szIP[100]; addr.ToStringBuffer(szIP, 100); Logging::LogMsg(LL_VERBOSE, "Rate Limiter has blocked incoming connection from IP %s", szIP); } ChkIf(false, E_FAIL); // this will trigger the socket to be immediately closed } } // -------------------------- clientsock = socktmp; pConn = _connectionpool.GetConnection(clientsock, role); ChkIfA(pConn == NULL, E_FAIL); // Our connection pool has nothing left to give, only thing to do is abort this connection and close the socket socktmp = -1; ChkA(pConn->_stunsocket.SetNonBlocking(true)); ChkA(_spPolling->Add(clientsock, EPOLL_CLIENT_READ_EVENT_SET)); // add connection to our tracking tables pConn->_idHashTable = (_pNewConnList == &_hashConnections1) ? 1 : 2; insertresult = _pNewConnList->Insert(clientsock, pConn); // out of space in the lookup tables? ChkIfA(insertresult == -1, E_FAIL); if (Logging::GetLogLevel() >= LL_VERBOSE) { char szIPRemote[100]; char szIPLocal[100]; pConn->_stunsocket.GetLocalAddress().ToStringBuffer(szIPLocal, ARRAYSIZE(szIPLocal)); pConn->_stunsocket.GetRemoteAddress().ToStringBuffer(szIPRemote, ARRAYSIZE(szIPRemote)); Logging::LogMsg(LL_VERBOSE, "accepting new connection on socket %d from %s on interface %s", pConn->_stunsocket.GetSocketHandle(), szIPRemote, szIPLocal); } Cleanup: if (FAILED(hr)) { CloseConnection(pConn); pConn = NULL; if (socktmp != -1) { close(socktmp); } } return pConn; }
// This test validates that the EnablePktInfoOption set on a socket allows us to get at the destination IP address for incoming packets // This is needed so that we can correctly insert an origin address into responses from the server // Otherwise, the server doesn't have a way of knowing which interface a packet arrived on when it's listening on INADDR_ANY (all available addresses) HRESULT CTestRecvFromEx::DoTest(bool fIPV6) { // create a couple of sockets for send/recv HRESULT hr = S_OK; CSocketAddress addrAny(0,0); // INADDR_ANY, random port sockaddr_in6 addrAnyIPV6 = {}; uint16_t portRecv = 0; CRefCountedStunSocket spSocketSend, spSocketRecv; fd_set set = {}; CSocketAddress addrDestForSend; CSocketAddress addrDestOnRecv; CSocketAddress addrSrcOnRecv; CSocketAddress addrSrc; CSocketAddress addrDst; char ch = 'x'; sockaddr_storage addrDummy; socklen_t addrlength; int ret; timeval tv = {}; if (fIPV6) { addrAnyIPV6.sin6_family = AF_INET6; addrAny = CSocketAddress(addrAnyIPV6); } // create two sockets listening on INADDR_ANY. One for sending and one for receiving ChkA(CStunSocket::Create(addrAny, RolePP, &spSocketSend)); ChkA(CStunSocket::Create(addrAny, RolePP, &spSocketRecv)); spSocketRecv->EnablePktInfoOption(true); portRecv = spSocketRecv->GetLocalAddress().GetPort(); // now send to localhost if (fIPV6) { sockaddr_in6 addr6 = {}; addr6.sin6_family = AF_INET6; ::inet_pton(AF_INET6, "::1", &(addr6.sin6_addr)); addrDestForSend = CSocketAddress(addr6); } else { sockaddr_in addr4 = {}; addr4.sin_family = AF_INET; ::inet_pton(AF_INET, "127.0.0.1", &(addr4.sin_addr)); addrDestForSend = CSocketAddress(addr4); } addrDestForSend.SetPort(portRecv); // flush out any residual packets that might be buffered up on recv socket ret = -1; do { addrlength = sizeof(addrDummy); ret = ::recvfrom(spSocketRecv->GetSocketHandle(), &ch, sizeof(ch), MSG_DONTWAIT, (sockaddr*)&addrDummy, &addrlength); } while (ret >= 0); // now send some data to ourselves ret = sendto(spSocketSend->GetSocketHandle(), &ch, sizeof(ch), 0, addrDestForSend.GetSockAddr(), addrDestForSend.GetSockAddrLength()); ChkIfA(ret <= 0, E_UNEXPECTED); // now wait for the data to arrive FD_ZERO(&set); FD_SET(spSocketRecv->GetSocketHandle(), &set); tv.tv_sec = 3; ret = select(spSocketRecv->GetSocketHandle()+1, &set, NULL, NULL, &tv); ChkIfA(ret <= 0, E_UNEXPECTED); ret = ::recvfromex(spSocketRecv->GetSocketHandle(), &ch, 1, MSG_DONTWAIT, &addrSrcOnRecv, &addrDestOnRecv); ChkIfA(ret <= 0, E_UNEXPECTED); ChkIfA(addrSrcOnRecv.IsIPAddressZero(), E_UNEXPECTED); ChkIfA(addrDestOnRecv.IsIPAddressZero(), E_UNEXPECTED); 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; }
HRESULT GetSocketAddressForAdapter(int family, const char* pszAdapterName, uint16_t port, CSocketAddress* pSocketAddr) { HRESULT hr = S_OK; ifaddrs* pList = NULL; ifaddrs* pAdapter = NULL; ifaddrs* pAdapterFound = NULL; ChkIfA(pszAdapterName == NULL, E_INVALIDARG); ChkIfA(pszAdapterName[0] == '\0', E_INVALIDARG); ChkIfA(pSocketAddr == NULL, E_INVALIDARG); // what if the socket address is available, but not "up". Well, just let this call succeed. If the server errors out, it will get cleaned up then ChkIf(getifaddrs(&pList) < 0, ERRNOHR); pAdapter = pList; while (pAdapter) { if ((pAdapter->ifa_addr != NULL) && (pAdapter->ifa_name != NULL) && (family == pAdapter->ifa_addr->sa_family)) { if (strcmp(pAdapter->ifa_name, pszAdapterName) == 0) { pAdapterFound = pAdapter; break; } } pAdapter = pAdapter->ifa_next; } // If pszAdapterName is an IP address, convert it into a sockaddr and compare the address field with that of the adapter // Note: an alternative approach would be to convert pAdapter->ifa_addr to a string and then do a string compare. // But then it would be difficult to match "::1" with "0:0:0:0:0:0:0:1" and other formats of IPV6 strings if ((pAdapterFound == NULL) && ((family == AF_INET) || (family == AF_INET6)) ) { uint8_t addrbytes[sizeof(in6_addr)] = {}; int comparesize = (family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr); void* pCmp = NULL; if (inet_pton(family, pszAdapterName, addrbytes) == 1) { pAdapter = pList; while (pAdapter) { if ((pAdapter->ifa_addr != NULL) && (family == pAdapter->ifa_addr->sa_family)) { // offsetof(sockaddr_in, sin_addr) != offsetof(sockaddr_in6, sin6_addr) // so you really can't do too many casting tricks like you can with sockaddr and sockaddr_in if (family == AF_INET) { sockaddr_in *pAddr4 = (sockaddr_in*)(pAdapter->ifa_addr); pCmp = &(pAddr4->sin_addr); } else { sockaddr_in6 *pAddr6 = (sockaddr_in6*)(pAdapter->ifa_addr); pCmp = &(pAddr6->sin6_addr); } if (memcmp(pCmp, addrbytes, comparesize) == 0) { // match on ip address string found pAdapterFound = pAdapter; break; } } pAdapter = pAdapter->ifa_next; } } } ChkIf(pAdapterFound == NULL, E_FAIL); { *pSocketAddr = CSocketAddress(*(pAdapterFound->ifa_addr)); pSocketAddr->SetPort(port); } Cleanup: freeifaddrs(pList); return hr; }
HRESULT CTestClientLogic::CommonInit(NatBehavior behavior, NatFiltering filtering) { HRESULT hr = S_OK; CSocketAddress addrMapped(0x22222222, 6000); _addrServerPP = CSocketAddress(0xaaaaaaaa, 1001); _addrServerPA = CSocketAddress(0xaaaaaaaa, 1002); _addrServerAP = CSocketAddress(0xbbbbbbbb, 1001); _addrServerAA = CSocketAddress(0xbbbbbbbb, 1002); _tsa.set[RolePP].fValid = true; _tsa.set[RolePP].addr = _addrServerPP; _tsa.set[RolePA].fValid = true; _tsa.set[RolePA].addr = _addrServerPA; _tsa.set[RoleAP].fValid = true; _tsa.set[RoleAP].addr = _addrServerAP; _tsa.set[RoleAA].fValid = true; _tsa.set[RoleAA].addr = _addrServerAA; _addrLocal = CSocketAddress(0x33333333, 7000); _addrMappedPP = addrMapped; _addrMappedPA = addrMapped; _addrMappedAP = addrMapped; _addrMappedAA = addrMapped; _spClientLogic = boost::shared_ptr<CStunClientLogic>(new CStunClientLogic()); switch (behavior) { case DirectMapping: { _addrMappedPP = _addrLocal; _addrMappedPA = _addrLocal; _addrMappedAP = _addrLocal; _addrMappedAA = _addrLocal; break; } case EndpointIndependentMapping: { break; } case AddressDependentMapping: { _addrMappedAP.SetPort(6002); _addrMappedAA.SetPort(6002); break; } case AddressAndPortDependentMapping: { _addrMappedPA.SetPort(6001); _addrMappedAP.SetPort(6002); _addrMappedAA.SetPort(6003); break; } default: { ChkA(E_FAIL); } } switch (filtering) { case EndpointIndependentFiltering: case DirectConnectionFiltering: { _fAllowChangeRequestPA = true; _fAllowChangeRequestAA = true; break; } case AddressDependentFiltering: { _fAllowChangeRequestPA = true; _fAllowChangeRequestAA = false; break; } case AddressAndPortDependentFiltering: { _fAllowChangeRequestPA = false; _fAllowChangeRequestAA = false; break; } default: { ChkA(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; }
HRESULT CreateConfigFromCommandLine(ClientCmdLineArgs& args, StunClientLogicConfig* pConfig, ClientSocketConfig* pSocketConfig) { HRESULT hr = S_OK; StunClientLogicConfig& config = *pConfig; ClientSocketConfig& socketconfig = *pSocketConfig; int ret; uint16_t localport = 0; uint16_t remoteport = 0; int nPort = 0; char szIP[100]; bool fTCP = false; config.fBehaviorTest = false; config.fFilteringTest = false; config.fTimeoutIsInstant = false; config.timeoutSeconds = 0; // use default config.uMaxAttempts = 0; socketconfig.family = AF_INET; socketconfig.socktype = SOCK_DGRAM; socketconfig.addrLocal = CSocketAddress(0, 0); ChkIfA(pConfig == NULL, E_INVALIDARG); ChkIfA(pSocketConfig==NULL, E_INVALIDARG); // family (protocol type) ------------------------------------ if (StringHelper::IsNullOrEmpty(args.strFamily.c_str())==false) { int optvalue = atoi(args.strFamily.c_str()); switch (optvalue) { case 4: socketconfig.family = AF_INET; break; case 6: socketconfig.family = AF_INET6; break; default: { Logging::LogMsg(LL_ALWAYS, "Family option must be either 4 or 6"); Chk(E_INVALIDARG); } } } // protocol -------------------------------------------- StringHelper::ToLower(args.strProtocol); if (StringHelper::IsNullOrEmpty(args.strProtocol.c_str()) == false) { if ((args.strProtocol != "udp") && (args.strProtocol != "tcp")) { Logging::LogMsg(LL_ALWAYS, "Only udp and tcp are supported protocol versions"); Chk(E_INVALIDARG); } if (args.strProtocol == "tcp") { fTCP = true; socketconfig.socktype = SOCK_STREAM; config.uMaxAttempts = 1; } } // remote port --------------------------------------------- if (StringHelper::IsNullOrEmpty(args.strRemotePort.c_str()) == false) { ret = StringHelper::ValidateNumberString(args.strRemotePort.c_str(), 1, 0xffff, &nPort); if (ret < 0) { Logging::LogMsg(LL_ALWAYS, "Remote port must be between 1 - 65535"); Chk(E_INVALIDARG); } remoteport = (uint16_t)(unsigned int)nPort; } else { remoteport = DEFAULT_STUN_PORT; } // remote server ----------------------------------------- if (StringHelper::IsNullOrEmpty(args.strRemoteServer.c_str())) { Logging::LogMsg(LL_ALWAYS, "No server address specified"); Chk(E_INVALIDARG); } hr = ::ResolveHostName(args.strRemoteServer.c_str(), socketconfig.family, false, &config.addrServer); if (FAILED(hr)) { Logging::LogMsg(LL_ALWAYS, "Unable to resolve hostname for %s", args.strRemoteServer.c_str()); Chk(hr); } config.addrServer.ToStringBuffer(szIP, ARRAYSIZE(szIP)); Logging::LogMsg(LL_DEBUG, "Resolved %s to %s", args.strRemoteServer.c_str(), szIP); config.addrServer.SetPort(remoteport); // local port -------------------------------------------- if (StringHelper::IsNullOrEmpty(args.strLocalPort.c_str()) == false) { ret = StringHelper::ValidateNumberString(args.strLocalPort.c_str(), 1, 0xffff, &nPort); if (ret < 0) { Logging::LogMsg(LL_ALWAYS, "Local port must be between 1 - 65535"); Chk(E_INVALIDARG); } localport = (uint16_t)(unsigned int)nPort; } // local address ------------------------------------------ if (StringHelper::IsNullOrEmpty(args.strLocalAddr.c_str()) == false) { hr = GetSocketAddressForAdapter(socketconfig.family, args.strLocalAddr.c_str(), localport, &socketconfig.addrLocal); if (FAILED(hr)) { Logging::LogMsg(LL_ALWAYS, "Unable to find matching adapter or interface for local address option"); Chk(hr); } } else { if (socketconfig.family == AF_INET6) { sockaddr_in6 addr6 = {}; addr6.sin6_family = AF_INET6; socketconfig.addrLocal = CSocketAddress(addr6); socketconfig.addrLocal.SetPort(localport); } else { socketconfig.addrLocal = CSocketAddress(0,localport); } } // mode --------------------------------------------- StringHelper::ToLower(args.strMode); if (StringHelper::IsNullOrEmpty(args.strMode.c_str()) == false) { if (args.strMode == "basic") { ; } else if (args.strMode == "full") { config.fBehaviorTest = true; config.fFilteringTest = (fTCP == false); // impossible to to a filtering test in TCP } else { Logging::LogMsg(LL_ALWAYS, "Mode option must be 'full' or 'basic'"); } } 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 CTCPServer::Initialize(const CStunServerConfig& config) { HRESULT hr = S_OK; TransportAddressSet tsaListenAll; TransportAddressSet tsaHandler; boost::shared_ptr<RateLimiter> spLimiter; 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 it can offer a CHANGED-ADDRESS attribute. InitTSA(&tsaHandler, RolePP, config.fHasPP, config.addrPP, config.addrPrimaryAdvertised); InitTSA(&tsaHandler, RolePA, config.fHasPA, config.addrPA, config.addrPrimaryAdvertised); InitTSA(&tsaHandler, RoleAP, config.fHasAP, config.addrAP, config.addrAlternateAdvertised); InitTSA(&tsaHandler, RoleAA, config.fHasAA, config.addrAA, config.addrAlternateAdvertised); InitTSA(&tsaListenAll, RolePP, config.fHasPP, config.addrPP, CSocketAddress()); InitTSA(&tsaListenAll, RolePA, config.fHasPA, config.addrPA, CSocketAddress()); InitTSA(&tsaListenAll, RoleAP, config.fHasAP, config.addrAP, CSocketAddress()); InitTSA(&tsaListenAll, RoleAA, config.fHasAA, config.addrAA, CSocketAddress()); if (config.fEnableDosProtection) { spLimiter = boost::shared_ptr<RateLimiter>(new RateLimiter(20000, config.fMultiThreadedMode)); } if (config.fMultiThreadedMode == false) { _threads[0] = new CTCPStunThread(); ChkA(_threads[0]->Init(tsaListenAll, tsaHandler, _spAuth, config.nMaxConnections, spLimiter)); } else { for (int threadindex = 0; threadindex < 4; threadindex++) { if (tsaHandler.set[threadindex].fValid) { TransportAddressSet tsaListen = tsaListenAll; // Since we already initialized tsaListenAll above, // make a copy and uninit each one that isn't going to be managed // by the thread we are about to create for (int temp = 0; temp < 4; temp++) { if (temp != threadindex) { tsaListen.set[temp].fValid = false; tsaListen.set[temp].addr = CSocketAddress(); } } _threads[threadindex] = new CTCPStunThread(); Chk(_threads[threadindex]->Init(tsaListen, tsaHandler, _spAuth, config.nMaxConnections, spLimiter)); } } } Cleanup: if (FAILED(hr)) { Shutdown(); } 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; }