Exemplo n.º 1
0
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());
        }
    }

}
HRESULT CStunThreadMessageHandler::ProcessBindingRequest(CStunMessageReader& reader)
{

    HRESULT hrTmp;
    bool fRequestHasPaddingAttribute = false;
    SocketRole socketOutput = _message.localSocket;
    StunChangeRequestAttribute changerequest = {};
    bool fSendOtherAddress = false;
    bool fSendOriginAddress = false;
    SocketRole socketOther;
    CSocketAddress addrOrigin;
    CSocketAddress addrOther;
    CStunMessageBuilder builder;
    uint16_t paddingSize = 0;
    bool fLegacyFormat = false; // set to true if the client appears to be rfc3489 based instead of based on rfc 5789


    _spResponseBuffer->SetSize(0);
    builder.GetStream().Attach(_spResponseBuffer, true);
    
    fLegacyFormat = reader.IsMessageLegacyFormat();

    // check for an alternate response port
    // check for padding attribute (todo - figure out how to inject padding into the response)
    // check for a change request and validate we can do it.  If so, set _socketOutput.  If not, fill out _error and return.
    // determine if we have an "other" address to notify the caller about


    // did the request come with a padding request
    if (SUCCEEDED(reader.GetPaddingAttributeSize(&paddingSize)))
    {
        // todo - figure out how we're going to get the MTU size of the outgoing interface
        fRequestHasPaddingAttribute = true;
    }

    // as per 5780, section 6.1, If the Request contained a PADDING attribute...
    // "If the Request also contains the RESPONSE-PORT attribute the server MUST return an error response of type 400."
    if (_fRequestHasResponsePort && fRequestHasPaddingAttribute)
    {
        _error.errorcode = STUN_ERROR_BADREQUEST;
        return E_FAIL;
    }

    // handle change request logic and figure out what "other-address" attribute is going to be
    if (SUCCEEDED(reader.GetChangeRequest(&changerequest)))
    {
        if (changerequest.fChangeIP)
        {
            socketOutput = SocketRoleSwapIP(socketOutput);
        }
        if(changerequest.fChangePort)
        {
            socketOutput = SocketRoleSwapPort(socketOutput);
        }

        // IsValidSocketRole just validates the enum, not whether or not we can send on it
        ASSERT(IsValidSocketRole(socketOutput));

        // now, make sure we have the ability to send from another socket
        if (_spStunResponder->HasAddress(socketOutput) == false)
        {
            // send back an error.  We're being asked to respond using another address that we don't have a socket for
            _error.errorcode = STUN_ERROR_BADREQUEST;
            return E_FAIL;
        }
    }

    // If we're only working one socket, then that's ok, we just don't send back an "other address" unless we have all four sockets confgiured

    // now here's a problem.  If we binded to "INADDR_ANY", all of the sockets will have "0.0.0.0" for an address (same for IPV6)
    // So we effectively can't send back "other address" if don't really know our own IP address
    // Fortunately, recvfromex and the ioctls on the socket allow address discovery a bit better

    fSendOtherAddress = (_spStunResponder->HasAddress(RolePP) && _spStunResponder->HasAddress(RolePA) && _spStunResponder->HasAddress(RoleAP) && _spStunResponder->HasAddress(RoleAA));

    if (fSendOtherAddress)
    {
        socketOther = SocketRoleSwapIP(SocketRoleSwapPort(_message.localSocket));

        hrTmp = _spStunResponder->GetSocketAddressForRole(socketOther, &addrOther);
        ASSERT(SUCCEEDED(hrTmp));

        // so if our ip address is "0.0.0.0", disable this attribute
        fSendOtherAddress = (SUCCEEDED(hrTmp) && (addrOther.IsIPAddressZero()==false));
    }

    // What's our address origin?
    VERIFY(SUCCEEDED(_spStunResponder->GetSocketAddressForRole(socketOutput, &addrOrigin)));
    if (addrOrigin.IsIPAddressZero())
    {
        // Since we're sending back from the IP address we received on, we can just use the address the message came in on
        // Otherwise, we don't actually know it
        if (socketOutput == _message.localSocket)
        {
            addrOrigin = _message.localAddr;
        }
    }
    fSendOriginAddress = (false == addrOrigin.IsIPAddressZero());

    // Success - we're all clear to build the response

    _socketOutput = socketOutput;

    _spResponseBuffer->SetSize(0);
    builder.GetStream().Attach(_spResponseBuffer, true);

    builder.AddHeader(StunMsgTypeBinding, StunMsgClassSuccessResponse);
    builder.AddTransactionId(_transid);
    builder.AddMappedAddress(_message.remoteAddr);

    if (fLegacyFormat == false)
    {
        builder.AddXorMappedAddress(_message.remoteAddr);
    }

    if (fSendOriginAddress)
    {
        builder.AddResponseOriginAddress(addrOrigin);
    }

    if (fSendOtherAddress)
    {
        builder.AddOtherAddress(addrOther, fLegacyFormat); // pass true to send back CHANGED-ADDRESS, otherwise, pass false to send back OTHER-ADDRESS
    }
    
    // finally - if we're supposed to have a message integrity attribute as a result of authorization, add it at the very end
    if (_integrity.fSendWithIntegrity)
    {
        if (_integrity.fUseLongTerm == false)
        {
            builder.AddMessageIntegrityShortTerm(_integrity.szPassword);
        }
        else
        {
            builder.AddMessageIntegrityLongTerm(_integrity.szUser, _integrity.szRealm, _integrity.szPassword);
        }
    }

    builder.FixLengthField();

    return S_OK;
}
Exemplo n.º 3
0
// 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;
}