Skip to content

Attended Transfer fails with 400 (null) #1459

@FinHorsley

Description

@FinHorsley

Hi all 👋

Happy to raise a PR for the following, but wanted to raise an issue first in case i've got the wrong end of the stick!

Context

SipSorcery running as a SipCallServer, similar to https://github.com/sipsorcery-org/sipsorcery/tree/master/examples/SIPExamples/SIPCallServer.

Issue

Attended transfers are failing with UAS call failed with status 400(null) if there are >= 2 UserAgents active (in-memory).

The attended transfer for the UserAgent with the same m_sipDialogue?.CallId as the Replaces CallId handles the Transfer correctly, however the other UserAgents (where the CallIds don't match) reject the transfer returning a 400. This becomes a timing problem, and if one of the other UserAgents responds quicker such that it's the first to return the 400, we get problems

In testing I've updated the code to just ignore the attended transfer if the CallIds don't match, rather than Rejecting the request, and this has solved our problems

Ignore the LogWarnings, they were just to make debugging easier locally

private async Task SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
{
    var traceId = Guid.NewGuid().ToString("N").Substring(0, 8);

    logger.LogWarning($"{traceId} Enter - Request {sipRequest.Method} - CallId: {sipRequest.Header.CallId}. m_sipDialogue CallId: {m_sipDialogue?.CallId}");

    if (m_sipDialogue != null)
    {
        if (sipRequest.Header.From != null &&
            sipRequest.Header.From.FromTag != null &&
            sipRequest.Header.To != null &&
            sipRequest.Header.To.ToTag != null &&
            sipRequest.Header.CallId == m_sipDialogue.CallId)
        {
            try
            {
                await DialogRequestReceivedAsync(sipRequest).ConfigureAwait(false);
            }
            catch (Exception excp)
            {
                // There no point bubbling this exception up. The next class up is the transport layer and
                // it doesn't know what to do if a request can't be dealt with.
                logger.LogError(excp, "Exception SIPUserAgent.SIPTransportRequestReceived. {ErrorMessage}", excp.Message);
            }
        }
        else if (sipRequest.Method == SIPMethodsEnum.INVITE && !string.IsNullOrWhiteSpace(sipRequest.Header.Replaces))
        {
            // An attended transfer INVITE should only be accepted if the dialog parameters in the Replaces header
            // match the current dialog. But... to be more accepting with only a small increase in risk we only
            // require a match on the Call-ID (only small increase in risk as if a malicious party can get 1
            // of the three required headers they can almost certainly get all 3).
            SIPReplacesParameter replaces = SIPReplacesParameter.Parse(sipRequest.Header.Replaces);

            if (replaces?.CallID != m_sipDialogue?.CallId)
            {
                logger.LogWarning("{traceId} Ignoring AttendedTransfer as Replaces header isn't for this dialogue. Replaces CallID {replacesCallId}, Dialogue CallID {dialogueCallId}", traceId, replaces.CallID, m_sipDialogue?.CallId);
                return;
            }

            logger.LogWarning("{traceId} Replaces header received, accepting Call as attended transfer", traceId);

            // This is a special case of receiving an INVITE request that is part of an attended transfer and
            // that if successful will replace the existing dialog.
            //UASInviteTransaction uasTx = new UASInviteTransaction(m_transport, sipRequest, null);
            var uas = AcceptCall(sipRequest);

            logger.LogWarning("{traceId} Accepting attended transfer", traceId);
            await AcceptAttendedTransfer(uas).ConfigureAwait(false);
        }
    }

    // Omitted for brevity

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions