Multi-part SDP and non-Lync endpoints

Lync endpoints, including UCMA applications, are capable of communicating directly with non-Lync SIP endpoints under certain circumstances, without going through the Mediation Server. This can be advantageous for a few reasons. For one, the Mediation Server removes non-standard SIP headers when passing along messages, so any information you try to include in custom SIP headers when connecting to an endpoint through the Mediation Server will be lost. Also, you may need to communicate with other internal SIP systems -- a voice mail system, for example -- in an environment where the Mediation Server is not in use. The first limitation is codecs: the remote endpoint must support a codec that Lync supports, such as G.711. Beyond this, though, there are some other unique features of Lync's dialect of SIP that sometimes confuse or upset other SIP endpoints. One of these is Lync's use of multi-part SDP.

You can identify a SIP INVITE that contains multi-part SDP by looking at the Content-Type header in the request. Normally, a SIP INVITE that is designed to initiate a call has a content type of application/sdp. This is what most SIP endpoints will expect. Lync endpoints, however, often instead use the content type multipart/alternative. If you look at the body of a message with this content type, you will see that it is split into several parts, with what appears to be gibberish in between:

------=_NextPart_000_27C7_01CE45DC.5D4863F0
Content-Type: application/sdp
Content-Transfer-Encoding: 7bit
Content-ID: <eb8c50acd33a623b8010f97df13317d3@claritycon.com>
Content-Disposition: session; handling=optional; ms-proxy-2007fallback
v=0
o=- 0 0 IN IP4 192.168.100.0
s=session
c=IN IP4 192.168.100.0
b=CT:99980
t=0 0
m=audio 50363 RTP/AVP 117 114 9 112 111 0 8 116 115 97 13 118 101
...
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
------=_NextPart_000_27C7_01CE45DC.5D4863F0
Content-Type: application/sdp
Content-Transfer-Encoding: 7bit
Content-ID: <4554325e494643e6f50b1a5e1bfb950b@claritycon.com>
Content-Disposition: session; handling=optional
v=0
o=- 0 1 IN IP4 192.168.100.0
s=session
c=IN IP4 192.168.100.0
b=CT:99980
t=0 0
...
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
------=_NextPart_000_27C7_01CE45DC.5D4863F0--

What is going on here? What's the reason for this bizarre behaviour from Lync?

The body of these messages is actually multipart MIME. The purpose of this multipart SDP is to provide a simpler version of the media offer that the older (OCS) endpoints can handle, while also including the current version for Lync endpoints. Unfortunately, non-Lync endpoints generally don't support the multipart/alternative content type for the SDP, and so to them it appears that the message is malformed or missing the SDP. To make matters worse, there is no way to disable this behaviour, even if you don't plan to support the 2007 versions of the client in your environment.

So how can we get ourselves out of this mess? An interesting option is to use the Lync Server SDK, specifically the Managed SIP Application API, to modify the INVITE message and turn the multi-part SDP into ordinary SDP. If you're looking for an explanation of the Managed SIP Application API and how to use it, check out my earlier post.

Here is an example of some code you might use to convert the multipart SDP in an outgoing INVITE to ordinary SDP (I'm assuming here that the MSPL script is only dispatching requests to the managed code when they need to be changed in this way). This is a very basic code sample, without any exception handling or anything, so obviously don't use it in a production environment.

[csharp] private void OnRequest(RequestReceivedEventArgs e) { Header contentType = e.Request.AllHeaders.FindFirst( Microsoft.Rtc.Sip.Header.StandardHeaderType.ContentType); Header contentLength = e.Request.AllHeaders.FindFirst( Microsoft.Rtc.Sip.Header.StandardHeaderType.ContentLength); string messageBody = e.Request.Content;

// Grab the boundary string from the Content-Type header var boundary = "--" + contentType.Value.Split('=').LastOrDefault();

// Grab the first MIME part that is long enough to contain SDP string firstSdpPart = requestContent.Split(new string[] { boundary }, StringSplitOptions.None).FirstOrDefault( sdp => sdp.Length > 10) ?? "";

// Remove the headers at the beginning of the MIME // part so we take only the SDP itself string newBody = firstSdpPart.Substring( firstSdpPart.IndexOf("v=", StringComparison.InvariantCulture)).Trim();

// Set the new content type to application/sdp contentType.Value = "application/sdp";

// Set the new content length to the actual length of // the new application/sdp body contentLength.Value = newBody.Length.ToString();

// Put the new body into the message e.Request.Content = newBody;

// Send the message along to its original destination e.ServerTransaction.CreateBranch().SendRequest(e.Request);

Console.WriteLine( "Changed multipart/alternative SDP to application/sdp"); } [/csharp]

With something like this in place, your Lync endpoints can gleefully send multipart SDP and it will be turned into normal SDP for the non-Lync endpoints on the other end.

Let me know if you have questions about this approach. Also, credit goes to my colleague Dan Gardiner, who worked together with me to figure out this solution and who wrote the test code that the sample above is based on.