Wednesday 24 July 2013

How to get the SIP response code in Asterisk 10+

In early Asterisk 1.8 you used to be able to get the SIP response code by using a dialplan entry like :-
exten => _X.,n,Set(SIPcause=${MASTER_CHANNEL(HASH(SIP_CAUSE,${CDR(dstchannel)}))}
The Asterisk developers discovered the way the information was being populated caused a significant performance hit and so decided to turn the feature off by default. It can be enabled by editing sip.conf and adding the following line

storesipcause=yes

From asterisk version 10 there is now a new way to get the SIP cause however the way in which it is read is a bit convoluted. Contrary to the example given on the official Asterisk WIKI you cannot query it in the channel which performed the dial. It has to be queried by the channel which actually generated the SIP response.
You therefore have to do a few things :-
  1. Add a hangup handler to the destination channel so that a dialplan routine is run when that channel hangs up.
  2. Use the new 'b' option to the Dial command which causes a dialplan routine to be called just before the dial happens and this is used to setup the hangup handler on the destination channel.
  3. In the hangup handler get a list of hangupcause strings which in most cases will be a single string which we can just use. If you simultaneously dialled two destinations you will have two strings however and will need to decide which to use.
  4. If the hangupcause string is blank then it probably just means the call was answered and was hungup normally so we can just return.
  5. Once we have the hangupcause to check read the SIP cause string.
  6. Now the problem of how to pass the variable back. Normal variable inheritance does not work properly because of the channel being hung up at this point so we use yet another new feature by setting a shared variable in the master channel.
  7. After the original dial we read the shared variable out of its own channel shared area and set a normal variable and then use the cut function to read the numerical value.
Finally here is a complete example :-


 [dial_sip]
exten => _X.,1,Dial(SIP/${ddi}@${carrier},,b(dial_sip^set_handler^1))
exten => _X.,n,Set(SIPcause=${SHARED(SIPcause)}, Responsetime=$[${EPOCH}-${dialtime}])
exten => _X.,n,Set(SIPcode=${CUT(SIPcause," ",2)})

exten => set_handler,1,Set(CHANNEL(hangup_handler_push)=dial_sip,outbound_handler,1)
exten => set_handler,n,Return()

exten => outbound_handler,1,NoOp(Destination channel has hungup)
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_KEYS()})
; If no hangup causes are available then its probably because it is a regular call and the call ended normally so we just return.
same => n,ExecIf($["${HANGUPCAUSE_STRING}" = ""]?Return())
same => n,NoOp(Got Channel ID ${HANGUPCAUSE_STRING} master ${MASTERCHANNEL} with Technology Cause Code ${HANGUPCAUSE(${HANGUPCAUSE_STRING},tech)}, Asterisk Cause Code ${HANGUPCAUSE(${HANGUPCAUSE_STRING},ast)})
same => n,Set(SHARED(SIPcause,${MASTERCHANNEL})=${HANGUPCAUSE(${HANGUPCAUSE_STRING},tech)})
same => n,Return()

2 comments:

  1. Well this did not work out for me. I had to supply the master channel as argument. You will have to replace the googletts stuff with your own way of signalling.

    [outboundsip]
    exten => s,1,Dial(${SIPCHAN}/${CALLEDNUM}@0435440806,25,gb(outboundsip^set_handler^1(${CHANNEL})))
    exten => s,2,Set(SIPcause=${SHARED(SIPcause):4})
    exten => s,3,Goto(s-${DIALSTATUS},1)

    exten => s-NOANSWER,1,Answer()
    exten => s-NOANSWER,2,Wait(1)
    exten => s-NOANSWER,3,agi(googletts.agi,"Called party did not answer.",en,1.4)
    exten => s-NOANSWER,4,Hangup()

    exten => s-CHANUNAVAIL,1,agi(googletts.agi,"All channels are busy.",en,1.4)
    exten => s-CHANUNAVAIL,2,agi(googletts.agi,"Please buy more channels or try later.",en,1.4)
    exten => s-CHANUNAVAIL,3,Hangup()

    exten => s-CONGESTION,1,Answer()
    exten => s-CONGESTION,2,Wait(1)
    exten => s-CONGESTION,3,agi(googletts.agi,"Congestion cause is ${SIPcause:4}",en,1.4)
    exten => s-CONGESTION,4,Hangup()

    exten => s-BUSY,1,Answer()
    exten => s-BUSY,2,Wait(1)
    exten => s-BUSY,3,agi(googletts.agi,"Busy cause is ${SIPcause:4}",en,1.4)
    exten => s-BUSY,4,Hangup()

    exten => s-ANSWER,1,Hangup()

    exten => set_handler,1,NoOp(${ARG1})
    exten => set_handler,2,Set(CHANNEL(hangup_handler_push)=outboundsip,outbound_handler,1(${ARG1}))
    exten => set_handler,n,Return()

    exten => outbound_handler,1,NoOp(Destination channel ${ARG1} has hungup)
    same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_KEYS()}) ; If no hangup causes are available then its probably because it is a regular call and the call ended normally so we just return.
    same => n,ExecIf($["${HANGUPCAUSE_STRING}" = ""]?Return())
    same => n,NoOp(Got Channel ID ${HANGUPCAUSE_STRING} master ${ARG1} with Technology Cause Code ${HANGUPCAUSE(${HANGUPCAUSE_STRING},tech)}, Asterisk Cause Code ${HANGUPCAUSE(${HANGUPCAUSE_STRING},ast)})
    same => n,Set(SHARED(SIPcause,${ARG1})=${HANGUPCAUSE(${HANGUPCAUSE_STRING},tech)})
    same => n,Return()

    ReplyDelete