STUN support

From reSIProcate
Jump to navigation Jump to search

Resiprocate implements STUN client and server support.

Introduction[edit]

STUN - Simple Traversal of User Datagram Protocol (UDP) Through Network Address Translators (NATs)

Defined by: RFC 3489

STUN Server[edit]

reSIProcate is able to act as an STUN server on each configured UDP transport. This means that reSIProcate will accept and respond to STUN requests from external clients. Usually STUN is done through a separate STUN server which operates on a different port (or even a different machine). The advantage of using STUN through the SIP port is that this may help traversing even symmetric NATs. Though, this method is not very popular currently and it doesn't really help if RTP streams are involved.

To activate STUN server support you must simply pass the "StunEnabled" value with the "AddTransport" method.


Note: The DialogUsageManager::AddTransport method simply maps through to the SipStack::AddTransport but with fewer parameters. Therefore probably it's best to use the method on the SipStack directly.


STUN Client[edit]

The UdpTransport class includes support for sending STUN requests to a STUN server. To use this feature you need to store a pointer to UdpTransport when adding the transport:


m_pUdpTransport = (resip::UdpTransport*)mStack->addTransport(UDP, UDPPort, V4, StunEnabled, IPAddress));


UdpTransport includes two methods for STUN client support:


bool stunSendTest(const Tuple& dest);
bool stunResult(Tuple& mappedAddress); 


The first one sends a STUN request to dest, the second one retrieves the result (if any).


Obviously this implementation is asynchronous. If you need to determine the mapped STUN address in a synchronous way (e.g. in order to make a call) you could use something similar to this:


void SendStunTest()
{
     if (!m_pUdpTransport) return;

     hostent* h = gethostbyname(STUNServer);
     in_addr sin_addr = *(struct in_addr*)h->h_addr;
     resip::Tuple tStunDest(sin_addr, STUNPort, UDP, Data::Empty);

     m_pUdpTransport->stunSendTest(tStunDest);

     mLastStunTestTime = GetTickCount();
}


..for sending the STUN request and the following to retrieve a result synchronously:


resip::Tuple GetStunAddress()
{
    resip::Tuple mMappedAddress;

    mMappedAddress.setPort(0);

    if (!m_pUdpTransport) return mMappedAddress;

    if (!m_pUdpTransport->stunResult(mMappedAddress))
    {
         // no valid result available, send another request
         SendStunTest();
    }

    else if ((GetTickCount() - mLastStunTestTime) > 1000 * 60 * 3) 
    {
         // don't use a STUN response that is older than 3 minutes
         SendStunTest();
    }

    DWORD dwTmpLastStunTestTime = mLastStunTestTime;

    while ((GetTickCount() - dwTmpLastStunTestTime) < 5 * 1000) // wait 5s for result
    {
         if (m_pUdpTransport->stunResult(mMappedAddress))
              break;
         Sleep(200);
    }

    return mMappedAddress;
}

Modifying messages for STUN[edit]

To change the contact's host and port you need to use

setOverrideHostAndPort

Modifying the via header is unfortunately a bit more difficult because the stack relies on the via header for transport selection. Therefore the via needs to be changed after transport selection.

To do this you must derive a class from MessageDecorator and implement the code to modify the message in this class.

Adding this MessageDecorator to the message itself doesn't help much because it wouldn't be used for messages that are generated by dum internally. Therefore we now have a new setOutboundDecorator member in the profile. Add your custom messagedecorator to the profile and it will automatically be added to each messsage that is sent out in the context of this profile.