예제 #1
0
/**
 * 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;
}
예제 #2
0
HRESULT CTestAtomicHelpers::Run()
{
    HRESULT hr = S_OK;
    
    int value = -2000;
    int nextexpected = -2000;
    int result = 0;
    
    while (value < 2000)
    {
        nextexpected++;
        result = AtomicIncrement(&value);
        ChkIf(result != nextexpected, E_UNEXPECTED);
        ChkIf(result != value, E_UNEXPECTED);
    }
    
    value = 2000;
    nextexpected = 2000;
    while (value > -2000)
    {
        nextexpected--;
        result = AtomicDecrement(&value);
        ChkIf(result != nextexpected, E_UNEXPECTED);
        ChkIf(result != value, E_UNEXPECTED);
    }

Cleanup:
    return hr;
}
예제 #3
0
HRESULT CTestMessageHandler::SendHelper(CStunMessageBuilder& builderRequest, CStunMessageReader* pReaderResponse, IStunAuth* pAuth)
{
    CRefCountedBuffer spBufferRequest;
    CRefCountedBuffer spBufferResponse(new CBuffer(MAX_STUN_MESSAGE_SIZE));
    StunMessageIn msgIn;
    StunMessageOut msgOut;
    CStunMessageReader reader;
    CSocketAddress addrDest;
    TransportAddressSet tas;
    HRESULT hr = S_OK;
    
    
    InitTransportAddressSet(tas, true, true, true, true);
    
    builderRequest.GetResult(&spBufferRequest);
    
    ChkIf(CStunMessageReader::BodyValidated != reader.AddBytes(spBufferRequest->GetData(), spBufferRequest->GetSize()), E_FAIL);
    
    msgIn.fConnectionOriented = false;
    msgIn.addrLocal = _addrServerPP;
    msgIn.pReader = &reader;
    msgIn.socketrole = RolePP;
    msgIn.addrRemote = _addrMapped;
    
    msgOut.spBufferOut = spBufferResponse;
    
    ChkA(CStunRequestHandler::ProcessRequest(msgIn, msgOut, &tas, pAuth));
    
    ChkIf(CStunMessageReader::BodyValidated != pReaderResponse->AddBytes(spBufferResponse->GetData(), spBufferResponse->GetSize()), E_FAIL);
    
Cleanup:
    
    return hr;
    
}
예제 #4
0
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;
}
예제 #5
0
// 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;
}
예제 #6
0
//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;
}
HRESULT CStunClientTestBase::BasicReaderValidation(CRefCountedBuffer& spMsg, CStunMessageReader& reader)
{
    HRESULT hr = S_OK;
    CStunMessageReader::ReaderParseState readerstate;
    StunTransactionId transid;
    int cmp = 0;

    readerstate = reader.AddBytes(spMsg->GetData(), spMsg->GetSize());
    ChkIf(readerstate != CStunMessageReader::BodyValidated, E_FAIL);

    reader.GetTransactionId(&transid);

    cmp = memcmp(transid.id, _transid.id, sizeof(_transid));

    ChkIf(cmp!=0, 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;
}
예제 #9
0
// 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;
}
예제 #10
0
HRESULT CTCPStunThread::CreatePipes()
{
    HRESULT hr = S_OK;
    int ret;
    
    ASSERT(_pipe[0] == -1);
    ASSERT(_pipe[1] == -1);
    
    ret = ::pipe(_pipe);
    
    ChkIf(ret == -1, ERRNOHR);
    
Cleanup:
    return hr;
}
예제 #11
0
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;
}
예제 #12
0
/**
 * Returns true if there are two or more host interfaces(adapters) for the specified family of IP addresses that are both "up" and not loopback adapters
 * @param family either AF_INET or AF_INET6
 */
bool HasAtLeastTwoAdapters(int family)
{
    HRESULT hr = S_OK;
    
    ifaddrs* pList = NULL;
    ifaddrs* pAdapter1 = NULL;
    ifaddrs* pAdapter2 = NULL;
    bool fRet = false;

    ChkIf(getifaddrs(&pList) < 0, ERRNOHR);

    GetDefaultAdapters(family, pList, &pAdapter1, &pAdapter2);

    fRet = (pAdapter1 && pAdapter2);


Cleanup:
    freeifaddrs(pList);
    return fRet;
}
예제 #13
0
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;
}
// 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;
}
예제 #15
0
HRESULT CTCPStunThread::WriteBytesForConnection(StunConnection* pConn)
{
    HRESULT hr = S_OK;
    int sock = pConn->_stunsocket.GetSocketHandle();
    int sent = -1;
    uint8_t* pData = NULL;
    size_t bytestotal, bytesremaining;
    bool fForceClose = false;
    int err;

    
    
    ASSERT(pConn != NULL);    
    
    pData = pConn->_spOutputBuffer->GetData();
    bytestotal = pConn->_spOutputBuffer->GetSize();
    
    
    while (true)
    {
        ASSERT(pConn->_state == ConnectionState_Transmitting);
        ASSERT(bytestotal > pConn->_txCount);
        
        bytesremaining = bytestotal - pConn->_txCount;
        
        sent = ::send(sock, pData + pConn->_txCount, bytesremaining, 0);

        err = errno;

        // Can't send any more bytes, come back again later
        ChkIf( ((sent == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))), S_OK);
        
        Logging::LogMsg(LL_VERBOSE, "send on socket %d returns %d (errno=%d)", sock, sent, (sent<0)?err:0);

        // general connection error
        ChkIf(sent == -1, E_FAIL);
        
        // can "send" ever return 0?
        ChkIfA(sent == 0, E_UNEXPECTED);
        
        pConn->_txCount += sent;
        
        // txCount should never exceed the total output message size, right?
        ASSERT(pConn->_txCount <= bytestotal);
        
        if (pConn->_txCount >= bytestotal)
        {
            pConn->_state = ConnectionState_Closing;
            
            shutdown(sock, SHUT_WR);
            // go back to listening for read events
            ChkA(_spPolling->ChangeEventSet(sock, EPOLL_CLIENT_READ_EVENT_SET));
            

            ConsumeRemoteClose(pConn);
            
            // so we can't assume the connection is still alive.  And if it's not alive, pConn likely got deleted
            // either refetch from the hash tables, or invent an out parameter on WriteBytesForConnection and ConsumeRemoteClose to better propagate the close state of the connection
            pConn = NULL;

            
            break;
        }
        // loop back and try to send the remaining bytes
    }
    
Cleanup:    
    if ((FAILED(hr) || fForceClose))
    {
        CloseConnection(pConn);
    }
    
    return hr;
}
예제 #16
0
HRESULT CTCPStunThread::ReceiveBytesForConnection(StunConnection* pConn)
{
    uint8_t buffer[MAX_STUN_MESSAGE_SIZE];
    size_t bytesneeded;
    int bytesread;
    HRESULT hr = S_OK;
    CStunMessageReader::ReaderParseState readerstate;
    int err;
    
    int sock = pConn->_stunsocket.GetSocketHandle();
    
    while (true)
    {
        ASSERT(pConn->_state == ConnectionState_Receiving);
        ASSERT(pConn->_reader.GetState() != CStunMessageReader::ParseError);
        ASSERT(pConn->_reader.GetState() != CStunMessageReader::BodyValidated);
        
        bytesneeded = pConn->_reader.HowManyBytesNeeded();
        
        ChkIfA(bytesneeded == 0, E_UNEXPECTED);
        
        bytesread = recv(sock, buffer, bytesneeded, 0);
        
        err = errno;
        Logging::LogMsg(LL_VERBOSE, "recv on socket %d returns %d (errno=%d)", sock, bytesread, (bytesread<0)?err:0);
        
        
        if ((bytesread < 0) && ((err == EWOULDBLOCK) || (err==EAGAIN)) )
        {
            // no more bytes to be consumed - bail out of here and return success
            break;
        }
        

        // any other error (or an EOF/shutdown notification) means the connection is dead
        ChkIf(bytesread <= 0, E_FAIL);
        
        // we got data, now let's feed it into the reader
        readerstate = pConn->_reader.AddBytes(buffer, bytesread);
        
        ChkIf(readerstate == CStunMessageReader::ParseError, E_FAIL);
        
        if (readerstate == CStunMessageReader::BodyValidated)
        {
            StunMessageIn msgIn;
            StunMessageOut msgOut;
            
            msgIn.addrLocal = pConn->_stunsocket.GetLocalAddress();
            msgIn.addrRemote = pConn->_stunsocket.GetRemoteAddress();
            msgIn.fConnectionOriented = true;
            msgIn.pReader = &pConn->_reader;
            msgIn.socketrole = pConn->_stunsocket.GetRole();
            
            msgOut.spBufferOut = pConn->_spOutputBuffer;
            
            Chk(CStunRequestHandler::ProcessRequest(msgIn, msgOut, &_tsa, _spAuth));
            
            // success - transition to the response state
            pConn->_state = ConnectionState_Transmitting;
            
            // change the socket such that we only listen for "write events"
            Chk(_spPolling->ChangeEventSet(sock, EPOLL_CLIENT_WRITE_EVENT_SET));
            
            // optimization - go ahead and try to send the response
            WriteBytesForConnection(pConn);
            // WriteBytesForConnection will close the connection on error
            // And it might call ConsumeRemoteClose, which will also null it out
            
            // so we can't assume the connection is still alive.  And if it's not alive, pConn likely got deleted
            // either refetch from the hash tables, or invent an out parameter on WriteBytesForConnection and ConsumeRemoteClose to better propagate the close state of the connection
            pConn = NULL;
            
            break;
        }
        
        // keep trying to read more bytes
    }
        
Cleanup:
    if (FAILED(hr))
    {
        CloseConnection(pConn);
    }

    return hr;
}
예제 #17
0
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;
}
예제 #18
0
void CStunThreadMessageHandler::ProcessRequest(StunMessageEnvelope& message)
{
    CStunMessageReader reader;
    CStunMessageReader::ReaderParseState state;
    uint16_t responsePort = 0;
    HRESULT hr = S_OK;


    ChkIfA(_spStunResponder == NULL, E_FAIL);

    _spReaderBuffer->SetSize(0);
    _spResponseBuffer->SetSize(0);
    _message = message;

    _addrResponse = message.remoteAddr;
    _socketOutput = message.localSocket;
    _fRequestHasResponsePort = false;
    
    // zero out _error without the overhead of zero'ing out every byte in the strings
    _error.errorcode = 0;
    _error.szNonce[0] = 0;
    _error.szRealm[0] = 0;
    _error.attribUnknown = 0;
    
    _integrity.fSendWithIntegrity = false;
    _integrity.szUser[0] = '\0';
    _integrity.szRealm[0] = '\0';
    _integrity.szPassword[0] = '\0';
    
    
    // attach the temp buffer to reader
    reader.GetStream().Attach(_spReaderBuffer, true);


    reader.SetAllowLegacyFormat(true);

    // parse the request
    state = reader.AddBytes(message.spBuffer->GetData(), message.spBuffer->GetSize());

    // If we get something that can't be validated as a stun message, don't send back a response
    // STUN RFC may suggest sending back a "500", but I think that's the wrong approach.
    ChkIf (state != CStunMessageReader::BodyValidated, E_FAIL);
    
    // Regardless of what we send back, let's always attempt to honor a response port request
    // Fix the destination port if the client asked for us to send back to another port
    if (SUCCEEDED(reader.GetResponsePort(&responsePort)))
    {
        _addrResponse.SetPort(responsePort);
        _fRequestHasResponsePort = true;
    }

    reader.GetTransactionId(&_transid);

    // ignore anything that is not a request (with no response)
    ChkIf(reader.GetMessageClass() != StunMsgClassRequest, E_FAIL);

    // pre-prep the error message in case we wind up needing to send it
    _error.msgtype = reader.GetMessageType();
    _error.msgclass = StunMsgClassFailureResponse;

    if (reader.GetMessageType() != StunMsgTypeBinding)
    {
        // we're going to send back an error response
        _error.errorcode = STUN_ERROR_BADREQUEST; // invalid request
    }
    else
    {
        // handle authentication - but only if an auth provider has been set
        hr = ValidateAuth(reader);

        // if auth succeeded, then carry on to handling the request
        if (SUCCEEDED(hr) && (_error.errorcode==0))
        {
            // handle the binding request
            hr = ProcessBindingRequest(reader);
        }

        // catch all for any case where an error occurred
        if (FAILED(hr) && (_error.errorcode==0))
        {
            _error.errorcode = STUN_ERROR_BADREQUEST;
        }
    }

    if (_error.errorcode != 0)
    {
        // if either ValidateAuth or ProcessBindingRequest set an errorcode, or a fatal error occurred
        SendErrorResponse();
    }
    else
    {
        SendResponse();
    }


Cleanup:
    return;
}
예제 #19
0
// 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;
}
예제 #20
0
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;

}