Partager via


Using STUN with the RTC Client API (Windows Embedded CE 6.0)

1/5/2010

In order to use a third party method to determine the NAT mappings of a client VoIP device's IP address and port, you can use the RTC Client API. Specifically, you will use the Port Manager API's to provide RTC with a callback function that gives RTC the NAT mappings for your device. This will allow you to dynamically determine and use the external NAT mapping that is being exposed for your VoIP device.

If your device only communicates within a network, meaning a private, internal network, then the default mappings that RTC obtains will work without modification. However, if you need to do NAT Traversal from an internal network to an external network, then the internal, private IP address and port your device is requesting and using in its communication is not the same as the external and publicly routable IP address and port that the NAT/Firewall is exposing. If the called party tried to reply back with the information you give it, it would be replying back with the internal, private IP address and port. The information would be invalid and the connection would fail. Therefore, in order to ensure that your SIP headers and SDP information is using the correct, public, routable IP address and port, you need to provide that information to RTC before it registers your device or begins a caller/callee session.

This is the situation where you might want to use Simple Traversal of UDP through NAT (STUN) or another method in order to determine the external, public and therefore routable IP address and port. This information, provided to RTC, will allow you to give the destination client the correct information so that you can establish a working peer-to-peer communication.

The Port Manager APIs provide a way for the application layer code to implement a callback Port Manager interface. The methods of this callback interface are called by the RTC Client in order to obtain the publicly routable address for unobstructed communication between clients bridged by a NAT.

For more information and a discussion about NAT Traversal and VoIP, please see NAT Traversal and VoIP.

For more information about NAT Traversal, please see NAT Traversal.

Overview of the Pertinent Port Manager Interfaces:

The Port Manager APIs facilitate RTC Client communication with external, not in the private network, devices. This includes outgoing and incoming calls, registration, subscription and notification services and IM.

The four main Port Manager COM interfaces that you can use to deal with NAT Traversal issues are the following:

The methods in IRTCPortManager3 include now a dependency on the remote client’s port as well as the transport used. This is in addition to the previous, existing dependency on the address pair.

IRTCProfilePortManagement exposes a method called SetPortManager which is similar to the one with the same name in IRTCSessionPortManagement. The only difference being that this new SetPortManager is for the Profile object rather than the Session object.

Responsibility of the Application Using the Port Manager API:

Depending on the type of NAT, you are mainly concerned with maintaining a mapping for a particular pair of addresses and transport protocol type. STUN will work in three out of four of the NAT cases, the only one it will not work with is Enterprise level NATs, commonly referred to as Symmetric NATs. In the other cases involving Full Cone NATs, Restricted Cone NATs and Port Restricted Cone NATs, a STUN client and STUN server combination should work fine.

Let's look at a very simple example. Some NATs provide a way to statically configure mapping. If this kind of pre-configuration is used, then you can implement the Port Manager callback interface in your application to return the addresses that are pre-configured and mapped back to RTC Client whenever the callback interface methods are called.

However, normally you will be dealing with a NAT that is dynamically changing the mappings. In this situation, your callback procedure, when called by RTC, will have to discover those IP address and port mappings, through a STUN server request, and then return them to RTC for use.

So you will have to implement in your application code the following:

  • Implement the methods for the callback interface IRTCPortManager3.
  • Have either a pre-configured mapping or a mechanism like STUN to discover and dynamically maintain a mapping that these methods will provide as the external mapping to the RTC Client.
  • Configure the RTC Session with the callback interface before establishing a session.
  • Configure the RTC Profile with the callback interface before registering to a Registrar server.

Obtaining NAT Mapping Information during Registration

You would implement the IRTCPortManager3 callback for the Profile so that you could supply the correct external NAT mappings before your device registers with the Registrar.

To get a better idea of the flow of control you would use, here is a code example that demonstrates the typical steps you would perform to set up a Profile callback for the Port Manager. Note the location of where you would independently make calls to your STUN server using whatever interface that server required in the code. This is the information you would obtain and then pass into RTC through the callback function.

Registration Example:

In this scenario, the application registers an RTC Client with a Registrar from behind a NAT. In general, the implementation steps are as follows:

  • The application creates a profile to be registered using IRTCClientProvisioning::CreateProfile.
  • The application queries the Profile object for the IRTCProfilePortManagement interface.
  • The application calls IRTCProfilePortManagement::SetPortManager to set its IRTCPortManager3 interface implementation for this session. In this example, our implementation is called AppPortManager3. The code for AppPortManager3 is not shown, but this is where you would make your STUN calls.
  • The RTC Client calls the provided AppPortManager3 methods to retrieve the external IP address and port pairs on the NAT and the internal port and address on the client device. It uses these port and address pairs for exchanging registration SIP messages with the Registrar.

This code is not complete, is untested and is for example use only.

//-------------------------------
// Registration from behind a NAT
//-------------------------------
HRESULT hr = S_OK;
IRTCClientProvisioning *pIRTCClientProvisioning = NULL;
IRTCProfile *pIRTCProfile = NULL;
BSTR bstrXMLProfile = // XML Blob referenced in different section
IRTCProfilePortManagement *pProfilePortManagement;
CComObject<AppPortManager3>* pIRTCPortManager3 = NULL; // App’s impl. of IRTCPortManager3
IRTCClient *pIRTCClient;

// Create a COM Object of AppPortManager3.
hr = CComObject<AppPortManager3>::CreateInstance(&pIRTCPortManager3);
// If (hr != S_OK), process the error here.

// CoCreate and initialize IRTCClient to pIRTCClient. 

// Perform QI for the Provisioning interface.
hr = pIRTCClient->QueryInterface(IID_IRTCClientProvisioning, reinterpret_cast<void **>(&pIRTCClientProvisioning));
// If (hr != S_OK), process the error here.

// Create the Profile object.
hr = pIRTCClientProvisioning->CreateProfile(bstrXMLProfile, &pIRTCProfile );
// If (hr != S_OK), process the error here.

// Query for the IRTCProfilePortManagement interface.
hr = pIRTCProfile->QueryInterface(IID_IRTCProfilePortManagement),(void **) &pProfilePortManagement);
// If (hr != S_OK), process the error here.

//Set the port manager call-back interface on the profile.
hr = pProfilePortManagement->SetPortManager(pIRTCPortManager3);

// Release the pointer to IRTCProfilePortManagement
// because we no longer need it.
...

// Enable the Profile and Register.
hr = pIRTCClientProvisioning->EnableProfile( pIRTCProfile, RTCRF_REGISTER_ALL);
// If (hr != S_OK), process the error here.

// RTC calls into the AppPortManager3::GetMappingEx()
// method to get the external SIP address and port.
// Here the application could answer back either with
// pre-configured external mapping details or could
// discover the external address and mapping using STUN.
...

Ee480771.collapse(en-US,WinEmbedded.60).gifObtaining NAT Mapping Information for Outgoing Calls

You would implement the IRTCPortManager3 callback for the Session so that you could supply the correct external NAT mappings before your device actually begins a session.

To get a better idea of the flow of control you would use, here is a code example that demonstrates the typical steps you would perform to set up a Session callback for the Port Manager. Note the location of where you would independently make calls to your STUN server using whatever interface that server required in the code. This is the information you would obtain and then pass into RTC through the callback function.

It is also important to note that even though the code is not shown in this example, you are setting up the Profile object as you did before, in the Registration example code, because you will need to provide mappings during registration as well as during any session you initiate.

You can use the same code, in these examples, AppPortManager3, for both callback situations. Any special requirements of your STUN or other method for retrieving the NAT mappings must be handled by you as appropriate in your own code.

Outgoing Call Example:

The steps in this scenario are same as any normal session initiation. The only changes are the usage of the extended callback interface in both the session and in the profile. See the code above for the Profile callback example.

This code is not complete, is untested and is for example use only.

//--------------------------------
// Outgoing call from behind a NAT
//--------------------------------
HRESULT hr = S_OK;
BSTR bstrLocalURI = NULL;
BSTR bstrDestURI = NULL;
RTC_SESSION_TYPE enSessionType;
IRTCSession *pIRTCSession = NULL;
IRTCSessionPortManagement *pSessionPortManagement;
CComObject<AppPortManager3>* pIRTCPortManager3 = NULL; // App’s impl. of IRTCPortManager3
IRTCProfile *pIRTCProfile = NULL;
IRTCClient *pIRTCClient;

enSessionType = // Specify session type

// Get the URI to call; this can be a sip: or a tel: URI.
bstrDestURI = ::SysAllocString(L"sip:someone@microsoft.com");

// Create a COM Object of AppPortManager3.
hr = CComObject<AppPortManager3>::CreateInstance(&pIRTCPortManager3);
// If (hr != S_OK), process the error here.

// The profile for pIRTCProfile is setup as explained in the
// example for registration!

// CoCreate and initialize IRTCClient to pIRTCClient.

// Create the session with the specified session type
// and local URI.
hr = pIRTCClient->CreateSession(enSessionType, bstrLocalURI, pIRTCProfile, RTCCS_FORCE_PROFILE, &pIRTCSession);

// If (hr != S_OK), process the error here.

// Query for the IRTCSessionPortManagement interface.
hr = pIRTCSession->QueryInterface(IID_IRTCSessionPortManagement,(void **)&pSessionPortManagement);

// If (hr != S_OK), process the error here.

//Set the Port Manager on the session.
// Note that the same Port Manager CallBack Interface can be used for
// both registration and Session establishment.
hr = pSessionPortManagement->SetPortManager(pIRTCPortManager3);

// Release the pointer to IRTCSessionPortManagement
// because we no longer need it.
...

IRTCParticipant *pIRTCParticipant = NULL;
hr = pIRTCSession->AddParticipant(bstrDestURI, NULL, &pIRTCParticipant);
// If (hr != S_OK), process the error here.

// RTC calls into the AppPortManager3::GetMappingEx() method multiple
// times to get the SIP and Media mapped addresses and ports.
// Note that RTC Client calls this method for each Port Type (SIP, RTP, RTCP)
// so the call-back interface should be ready to provide a mapped external
// address/port pair for each call.
// The application could answer back either with pre-configured external
// mapping details or could discover the external address and mapping using STUN.

...

// Free all the strings allocated with SysFreeString().
...

On a final note, please remember that the RTC Client will call GetMappingEx for each port type (SIP, RTP and RTCP) so you will need to supply the appropriate information back in your implementation of AppPortManager3 and provide a mapped external IP address and port pairing for each call.

See Also

Concepts

NAT Traversal and VoIP
Configuring RTC for Symmetric UDP for NAT Traversal

Other Resources

Common VoIP Phone Customization Tasks
IRTCPortManager3
IRTCProfilePortManagement
IRTCProfilePortManagement::SetPortManager
IRTCSessionPortManagement
Initialize RTC
RTC Client API Application Development