Thursday, March 6, 2014

SIP UUI Integration with Genesys Routing

Summary

This is the third in a series of articles I've written on integrating Genesys with SIP UUI.  Part 1 discussed the development of a UUI parsing web service.  Part 2 discussed how to invoke that web service from a Genesys routing strategy developed with IRD.  This last article will bring all the concepts together with an end-to-end example of extracting and depositing data to/from the UUI header with a Genesys routing strategy.

Environmentals

I'm using a Cisco router along with Genesys SIP Server (SIPS) for the SIP traffic.  In this previous article, I discussed the Cisco IOS VoIP trust list configuration necessary to integrate a Cisco router and Genesys SIPS.

In addition to the trust list, I'm going to need to do some hacking on the router to simulate a carrier bringing SIP UUI to Genesys via an IP trunk.  To do this, I'll utilize the Session Border Controller process on the router, aka Cisco Unified Border Element (CUBE).  Specifically, I need to artificially insert the 'User-to-User' header into the SIP INVITE sent to Genesys SIPS.

SIP headers can be modified easily in CUBE using sip-profile commands.  Unfortunately, the headers available for modification don't include the one I need - 'User-to-User'.  I suppose the reason for that is UUI isn't a standard, as of yet.  However, with some simple configuration we can hijack one of the headers that is supported by CUBE to get the UUI header inserted.  Below is the configuration to do just that with the Warning header.  The command below replaces the Warning header with a UUI-formatted data item.

voice class sip-profiles 300
 request INVITE sip-header Warning add "User-to-User:656e676c697368;encoding=hex"


Additionally, I had to add this profile to the dial-peer I'll use as the Genesys route point (5000).  That configuration is below.  The 'session target' is the IP address of Genesys SIPS.


dial-peer voice 30 voip

 destination-pattern 5000

 session protocol sipv2

 session target ipv4:192.168.1.69

 voice-class sip profiles 300

 codec g711ulaw



On the Genesys side, I made modifications to the SIPS application object to cause it to intercept the SIP 'User-to-User' header and insert it into the Genesys TLib message stream.  Figures 1 and 2 below depict the modifications I made to enable this SIP to TLib mapping.

Figure 1

Figure 2

Routing Strategy

Figure 3 is the routing strategy I used for this testing.  It is highly contrived (low to no production reality) and rigged to exercise the full Genesys SIP UUI functionality in a minimum of steps.

  1. Figure 4 is the Assign object used to assign the 'User-to-User' data item to script local variable.  The 'User-to-User' item was set via SIPS extracting that header item from the INVITE (SIP to TLib).
  2. I then call the Decoder web service to convert the hex-encoded data from the header to ASCII.
  3. A script List object is used to extract the first item from the resulting array returned by the web service object and assign it to a local variable.  The original 'User-to-User' item is also detached from the call.
  4. The Attach object then attaches that variable to the call (Figure 5).
  5. The next Web Service object (Figure 6) sets up a TLib to SIP mapping.  I'm hex encoding the string 'gold' and then assigning the result to the local array variable named 'callTypeList' (Figure 7).
  6. In Figure 8, three different function calls are being made.  The first establishes a 'User-to-User' header for the outgoing SIP INVITE.  This is the actual TLib to SIP mapping function.  The second function extracts the first value from the encode web service result array to a local variable.  The third function assigns the value of that local variable to that newly established  'User-to-User' header item.
  7. Finally, I route segment the call to a target that has a skill match with the initial UUI item.  That item was a language skill selection ('english' in this example).  Figure 9

Figure 3
Figure 4

Figure 5
Figure 6
Figure 7
Figure 8
Figure 9

Execution

Below are excerpts of the various logs during the strategy execution.

Cisco Router Log: 

UUI header has been inserted into the INVITE destined for Genesys SIPS.

INVITE sip:5000@192.168.1.69:5060 SIP/2.0
Via: SIP/2.0/UDP 10.10.10.1:5060;branch=z9hG4bK1144B
Remote-Party-ID: <sip:1234567890@10.10.10.1>;party=calling;screen=no;privacy=off
From: <sip:1234567890@10.10.10.1>;tag=A011F674-1EF9
To: <sip:5000@192.168.1.69>
Date: Thu, 06 Mar 2014 21:35:38 GMT
Call-ID: 1849BDBB-A4AE11E3-9987FE55-4FA7E03F@10.10.10.1
Supported: 100rel,timer,resource-priority,replaces,sdp-anat
Min-SE:  1800
Cisco-Guid: 0378126320-2762871267-2575498837-1336401983
User-Agent: Cisco-SIPGateway/IOS-15.2.4.M1
Allow: INVITE, OPTIONS, BYE, CANCEL, ACK, PRACK, UPDATE, REFER, SUBSCRIBE, NOTIFY, INFO, REGISTER
CSeq: 101 INVITE
Max-Forwards: 70
Timestamp: 1394141739
Contact: <sip:1234567890@10.10.10.1:5060>
Expires: 180
Allow-Events: telephone-event
Content-Type: application/sdp
Content-Disposition: session;handling=required
Content-Length: 209
User-to-User:656e676c697368;encoding=hex


SIPS Log:

SIPS receives the INVITE with the UUI header intact.


14:35:39.006: SIPTR: Received [0,UDP] 1131 bytes from 10.10.10.1:58029 <<<<<

INVITE sip:5000@192.168.1.69:5060 SIP/2.0^M

Via: SIP/2.0/UDP 10.10.10.1:5060;branch=z9hG4bK1144B^M

Remote-Party-ID: <sip:1234567890@10.10.10.1>;party=calling;screen=no;privacy=off^M

From: <sip:1234567890@10.10.10.1>;tag=A011F674-1EF9^M

To: <sip:5000@192.168.1.69>^M

Date: Thu, 06 Mar 2014 21:35:38 GMT^M

Call-ID: 1849BDBB-A4AE11E3-9987FE55-4FA7E03F@10.10.10.1^M

Supported: 100rel,timer,resource-priority,replaces,sdp-anat^M
Min-SE:  1800^M
Cisco-Guid: 0378126320-2762871267-2575498837-1336401983^M
User-Agent: Cisco-SIPGateway/IOS-15.2.4.M1^M
Allow: INVITE, OPTIONS, BYE, CANCEL, ACK, PRACK, UPDATE, REFER, SUBSCRIBE, NOTIFY, INFO, REGISTER^M
CSeq: 101 INVITE^M
Max-Forwards: 70^M
Timestamp: 1394141739^M
Contact: <sip:1234567890@10.10.10.1:5060>^M
Expires: 180^M
Allow-Events: telephone-event^M
Content-Type: application/sdp^M
Content-Disposition: session;handling=required^M
Content-Length: 209^M
User-to-User:656e676c697368;encoding=hex^M


URS Log:  

URS receives UUI data in hex format (SIP to TLib mapping).

14:35:39.009_I_I_0005024010bd6075 [01:01] call (2-981e1d0) for Resources created (del 1)
    _T_I_0005024010bd6075 [14:09] add DN TServer_SIPS810 5000 <5000@sips> (CDN 57 0005024010bd6075 97eb5b8) to the call 2-981e1d0 truly:22
received from 65200(TServer_SIPS810)genesys:7070(fd=) message EventRouteRequest
        AttributeCallState      0
        AttributeCallType       2
        AttributePropagatedCallType     2
        AttributeCallID 120
        AttributeConnID 0005024010bd6075
        AttributeCallUUID       '02ACMQ3C6S9GF9CM04000VTAES00003O'
        AttributeUserData       [46] 00 01 00 00..

                'User-to-User'  '656e676c697368;encoding=hex'



URS executes web service call to decode the hex to ASCII.

14:35:39.009_H_I_ [08:0c] SOAP request 2 sent to HTTP Bridge:
        URL:        http://192.168.1.75:8080/sipuui/services/UUITranscoder.UUITranscoderHttpSoap11Endpoint
        Method:     ns:decode
        NameSpace:  ns=http://sipuui
        SOAPaction: urn:decode
        Input:      ns:header:656e676c697368;encoding=hex
        Output:     decodeResponse.return
        HTTPAuthent:1
        SOAPSecrty:
14:35:39.009_I_I_0005024010bd6075 [09:04] <<<<<<<<<<<<suspend interp(WAIT_WEBSERVICE), func:GetWebServiceInfoEx timers:00000
14:35:39.015_H_I_0005024010bd6075 [08:08] OK InfoMessage (-1) is received from server ##HTTPSERVER, refid=2, hint=soap
  key V1 [List] value: (size=20)
    key STRN [String] value: "english"





URS assigns the ASCII value of the UUI item to a local variable and deletes the existing User-to-User item from the call.


14:35:39.015_I_I_0005024010bd6075 [09:05] >>>>>>>>>>>>resume interp(0), func:GetWebServiceInfoEx
    _I_I_0005024010bd6075 [09:04] ASSIGN: __WEBReturn(SCRIPT) <- LIST: V1.STRN:english
    _I_I_0005024010bd6075 [09:04] ASSIGN: languageList(SCRIPT) <- STRING: "1:english"
    _I_I_0005024010bd6075 [09:04] ASSIGN: language(SCRIPT) <- STRING: "english"
request to 65200(TServer_SIPS810) message RequestDeletePair
        AttributeReferenceID    14
        AttributeDataKey        'User-to-User'
        AttributeConnID 0005024010bd6075
        AttributeThisDN '5000'


URS creates/attaches key-value pair to the call.


request to 65200(TServer_SIPS810) message RequestAttachUserData
        AttributeReferenceID    15
        AttributeUserData       [22] 00 01 00 00..
                'LANGUAGE'      'english'
        AttributeConnID 0005024010bd6075
        AttributeThisDN '5000'


URS makes a second web service call to UUI format the string 'gold'.


14:35:39.015_H_I_ [08:0c] SOAP request 3 sent to HTTP Bridge:
        URL:        http://192.168.1.75:8080/sipuui/services/UUITranscoder.UUITranscoderHttpSoap11Endpoint
        Method:     ns:encode
        NameSpace:  ns=http://sipuui
        SOAPaction: urn:encode
        Input:      ns:values:gold
        Output:     encodeResponse.return
        HTTPAuthent:1
        SOAPSecrty:


URS assigns the result of the web service call to a local variable (callType).


14:35:39.018_H_I_0005024010bd6075 [08:08] OK InfoMessage (-1) is received from server ##HTTPSERVER, refid=3, hint=soap
  key V1 [List] value: (size=34)
    key STRN [String] value: "676f6c64;encoding=hex"
14:35:39.018_I_I_0005024010bd6075 [09:05] >>>>>>>>>>>>resume interp(0), func:GetWebServiceInfoEx
    _I_I_0005024010bd6075 [09:04] ASSIGN: __WEBReturn(SCRIPT) <- LIST: V1.STRN:676f6c64;encoding=hex
    _I_I_0005024010bd6075 [09:04] ASSIGN: callTypeList(SCRIPT) <- STRING: "1:676f6c64;encoding=hex"
    _I_I_0005024010bd6075 [09:04] ASSIGN: callType(SCRIPT) <- STRING: "676f6c64;encoding=hex"



URS targets Agent 1001 for the call.  Agent 1001 has a skill of 'english' with level > 1.  New User-to-User item is added to TLib Extensions.


14:35:39.019_T_I_0005024010bd6075 [14:19] send to ts TServer_SIPS810 RequestRouteCall to dn 1001 on  (dnis= )
request to 65200(TServer_SIPS810) message RequestRouteCall
        AttributeReferenceID    19
        AttributeReason [14] 00 01 01 00..
                'RTR'   132
        AttributeRouteType      0 (RouteTypeUnknown)
        AttributeExtensions     [230] 00 0a 00 00..
                'SIP_HEADERS'   'User-to-User'
                'User-to-User'  '676f6c64;encoding=hex'
                'CUSTOMER_ID'   'Resources'
                'AGENT' 'Employee_ID_1001'
                'PLACE' 'Place_1001'
                'DN'    '1001'
                'ACCESS'        '1001'
                'SWITCH'        'sips'
                'NVQ'   1
                'TARGET'        '?:english > 1@statserver.GA'
        AttributeOtherDN        '1001'
        AttributeConnID 0005024010bd6075
        AttributeThisDN '5000'



SIPS Log:  INVITE is sent to Agent 1001's endpoint.  New User-to-User item added to header (TLib to SIP mapping).


14:35:39.096: Sending  [0,UDP] 1306 bytes to 192.168.1.70:8440 >>>>>
INVITE sip:1001@192.168.1.70:8440;rinstance=eb8de7f330f64bc8 SIP/2.0^M
From: sip:1234567890@10.10.10.1;tag=0090D300-6C37-1307-A596-0100007FAA77-19268^M
To: <sip:5000@192.168.1.69:5060>^M
Call-ID: 0090D2D8-6C37-1307-A596-0100007FAA77-19132@192.168.1.69^M
CSeq: 1 INVITE^M
Content-Length: 212^M
Content-Type: application/sdp^M
Via: SIP/2.0/UDP 192.168.1.69:5060;branch=z9hG4bK0090D314-6C37-1307-A596-0100007FAA77-188^M
Contact: <sip:1234567890@192.168.1.69:5060>^M
X-Genesys-CallInfo: routed^M
User-to-User: 676f6c64;encoding=hex^M
Allow: ACK, BYE, CANCEL, INFO, INVITE, MESSAGE, NOTIFY, OPTIONS, PRACK, REFER, UPDATE^M
Remote-Party-ID: <sip:1234567890@10.10.10.1>;party=calling;screen=no;privacy=off^M
Date: Thu, 06 Mar 2014 21:35:38 GMT^M
Cisco-Guid: 0378126320-2762871267-2575498837-1336401983^M
User-Agent: Cisco-SIPGateway/IOS-15.2.4.M1^M


Support Phone Log:  Call arrives with the LANGUAGE attached data.
Figure 10

Wednesday, March 5, 2014

Cisco VoIP Trust Lists with Genesys SIP Server


Starting with Cisco IOS release 15.1(2)T, Cisco has changed the behavior of their voice gateways as it relates to SIP sources. The purpose of the change was to add an additional safeguard against toll fraud.

Prior to this release, the default behavior of a Cisco gateway was to allow any VoIP source to initiate call setup on the gateway. Now, you must explicitly configure a trust relationship on the gateway with any VoIP source that isn’t already configured in a dial peer.

Example:

x.x.x.x represents an instance of Genesys SIP server. Below I add that address to the trust list on my gateway.

voice service voip 
ip address trusted list ipv4 x.x.x.x 
allow-connections sip to sip
sip


With this configuration, call setups initiated from endpoints off Genesys SIP server work correctly.

If we remove the Genesys SIPS IP address entry from the trust list and turn up ccsip logging on the gateway, you’ll see the behavior below (403 Forbidden). In this scenario, I have a Counterpath SIP client (1001) registered to Genesys SIPS and attempting to outdial to a TF number.


#debug ccsip messages

.

.

.

Jun  8 07:24:52: //-1/xxxxxxxxxxxx/SIP/Msg/ccsipDisplayMsg:
Received:
INVITE
 sip:8008888756@x:5060 SIP/2.0
From:
 sip:1001@x;tag=0004FCE0-8685-1FBF-B162-0100007FAA77-20169
To: <
sip:8008888756@x:5060>
Call-ID:
 0004FCAE-8685-1FBF-B162-0100007FAA77-20163@x
CSeq: 1 INVITE
Content-Length: 138
Content-Type: application/sdp
Via: SIP/2.0/UDPx:5060;branch=z9hG4bK0004FCF4-8685-1FBF-B162-0100007FAA77-13
Contact: <
sip:1001@x:5060>
Allow: ACK, BYE, CANCEL, INFO, INVITE, MESSAGE, NOTIFY, OPTIONS, PRACK, REFER, UPDATE
User-Agent: X-Lite 4 release 4.1 stamp 63214
Max-Forwards: 69
X-Genesys-CallUUID: 009LS0K6GKFRVCB204000VTAES000004
X-ISCC-CofId: location=sips;cofid=8
Session-Expires: 1800;refresher=uac
Min-SE: 90
Supported: uui,100rel,timer

v=0
o=- 1337954333 1 IN IP4 192.168.1.70
s=CounterPath X-Lite 4.1
c=IN IP4 192.168.1.70
t=0 0
m=audio 61984 RTP/AVP 0 8
a=sendrecv

Jun  8 07:24:52: //41918/29DBBA08AB62/SIP/Msg/ccsipDisplayMsg:
Sent:
SIP/2.0 100 Trying
Via: SIP/2.0/UDP x:5060;branch=z9hG4bK0004FCF4-8685-1FBF-B162-0100007FAA77-13
From:
 sip:1001@x;tag=0004FCE0-8685-1FBF-B162-0100007FAA77-20169
To: <
sip:8008888756@x:5060>
Date: Fri, 08 Jun 2012 13:24:52 GMT
Call-ID:
 0004FCAE-8685-1FBF-B162-0100007FAA77-20163@192.168.1.69
CSeq: 1 INVITE
Allow-Events: telephone-event
Server: Cisco-SIPGateway/IOS-12.x
Content-Length: 0


Jun  8 07:24:52: //41918/29DBBA08AB62/SIP/Msg/ccsipDisplayMsg:
Sent:
SIP/2.0 403 Forbidden
Via: SIP/2.0/UDP 192.168.1.69:5060;branch=z9hG4bK0004FCF4-8685-1FBF-B162-0100007FAA77-13
From:
 sip:1001@x;tag=0004FCE0-8685-1FBF-B162-0100007FAA77-20169
To: <
sip:8008888756@x>;tag=23E3A1E4-2684
Date: Fri, 08 Jun 2012 13:24:52 GMT
Call-ID:
 0004FCAE-8685-1FBF-B162-0100007FAA77-20163@x

CSeq: 1 INVITE
Allow-Events: telephone-event
Server: Cisco-SIPGateway/IOS-12.x
Reason: Q.850;cause=21
Content-Length: 0


Turning up the lower level ccapi debugs reveals the cause of the 403:


#debug voip ccapi inout

Jun  8 07:30:05: //41926/E4D957C1AB72/CCAPI/cc_process_call_setup_ind:
>>>>CCAPI handed cid 41926 with tag 150 to app "_ManagedAppProcess_TOLLFRAUD_APP"
Jun  8 07:30:05: //41926/E4D957C1AB72/CCAPI/ccCallDisconnect:  Cause Value=21, Tag=0x0, Call Entry(Previous Disconnect Cause=0, Disconnect Cause=0)Jun  8 07:30:05: //41926/E4D957C1AB72/CCAPI/ccCallDisconnect:  Cause Value=21, Tag=0x0, Call Entry(Previous Disconnect Cause=0, Disconnect Cause=0)



Net, the trust list/call blocking functionality is default behavior now on IOS releases. If you upgrade a gateway to 15.1(2)T or higher and don’t account for it with additional trust list configuration, calls will be blocked.

Genesys Routing Integration with Web Services

Summary

This is a continuation of my previous article on SIP UUI header decoding.  In this article, I'll be demonstrating how to call that web service directly from a Genesys routing strategy developed in Interaction Routing Designer (IRD).

Environmentals

Genesys Universal Router Server (URS) has provided a two-way web interface (HTTP Bridge) for several years.  To enable the interface and its logging, you need to add a 'web' section to the Options configuration for the URS object.  Figures 1 and 2 below depict the Options configuration I used.

Figure 1
Figure 2
Side Note:  You must configure the 'http_port' option to enable URS to connect to external web servers.  That option opens a server socket on the server where URS resides.  I can't make sense of the logic behind a mandate to open a server socket on URS just to enable client sockets, but it is what it is.

Routing Strategy

Figure 3 depicts a toy routing strategy written with IRD.  The strategy demonstrates the basics of calling a web service.  I execute the following logic in this strategy:

  1. I configured a Web Service object to make a call to the UUI header 'decode' web service I developed and described here.  Filling in the required info for the General Tab (Figure 4) is accomplished easiest by simply importing the WSDL for your target web service.  Here I'm setting up the hex value of 3836372d35333039;encoding=hex as the input parameter for the web service call.  Figure 5 depicts the Result tab, which corresponds to the output.
  2. I use the Function object (Figure 6) to assign the web service result to a local variable.  Genesys URS assumes all web service results are arrays, so I'm using the List function to assign the first value of that array (phoneNumList) to a local variable (phoneNum).
  3. I use Multi-Attach object to attach the variable to the call (Figure 7).
  4. Finally, URS routes the call to a skill target.


Figure 3

Figure 4

Figure 5

Figure 6

Figure 7
Execution

Below are some log excerpts showing the events that unfold when this strategy was executed.  I placed a test call to DN 4000 where I had this strategy loaded.  The TServer was Genesys SIP Server. The agent environment was the X-Lite endpoint and Genesys Support Phone.

URS Log:

received from 65200(TServer_SIPS810)genesys:7070(fd=) message EventRouteRequest
        AttributeCallState      0
        AttributeCallType       2
        AttributePropagatedCallType     2
        AttributeCallID 116
        AttributeConnID 0005024010bd6071
        AttributeCallUUID       '02ACMQ3C6S9GF9CM04000VTAES00003K'
        AttributeDNIS   '4000'
        AttributeANI    '1111111111'
        AttributeThisDN '4000'
...
11:32:10.231_H_I_ [08:0c] SOAP request 2 sent to HTTP Bridge:
        URL:        http://192.168.1.75:8080/sipuui/services/UUITranscoder.UUITranscoderHttpSoap11Endpoint
        Method:     ns:decode
        NameSpace:  ns=http://sipuui
        SOAPaction: urn:decode
        Input:      ns:header:3836372d35333039;encoding=hex
        Output:     decodeResponse.return
        HTTPAuthent:1
        SOAPSecrty:
11:32:10.231_I_I_0005024010bd6071 [09:04] <<<<<<<<<<<<suspend interp(WAIT_WEBSERVICE), func:GetWebServiceInfoEx timers:00000
11:32:10.236_H_I_0005024010bd6071 [08:08] OK InfoMessage (-1) is received from server ##HTTPSERVER, refid=2, hint=soap
  key V1 [List] value: (size=21)
    key STRN [String] value: "867-5309"
11:32:10.236_I_I_0005024010bd6071 [09:05] >>>>>>>>>>>>resume interp(0), func:GetWebServiceInfoEx
    _I_I_0005024010bd6071 [09:04] ASSIGN: __WEBReturn(SCRIPT) <- LIST: V1.STRN:867-5309
    _I_I_0005024010bd6071 [09:04] ASSIGN: phoneNumList(SCRIPT) <- STRING: "1:867-5309"
    _I_I_0005024010bd6071 [09:04] ASSIGN: phoneNum(SCRIPT) <- STRING: "867-5309"
request to 65200(TServer_SIPS810) message RequestAttachUserData
        AttributeReferenceID    14
        AttributeUserData       [28] 00 01 00 00..
                'Jennys_Number' '867-5309'
        AttributeConnID 0005024010bd6071
        AttributeThisDN '4000'


HTTP Bridge Log:

03/04/14@11:32:10: [HTTP Client 85d32f4] Request sent:
POST /sipuui/services/UUITranscoder.UUITranscoderHttpSoap11Endpoint HTTP/1.1
Host: 192.168.1.75:8080
User-Agent: gSOAP/2.7
Content-Type: text/xml; charset=utf-8
Content-Length: 493
Connection: keep-alive
SOAPAction: "urn:decode"

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns="http://sipuui"><SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><ns:decode><ns:header>3836372d35333039;encoding=hex</ns:header></ns:decode></SOAP-ENV:Body></SOAP-ENV:Envelope>
03/04/14@11:32:10: [HTTP Client 85d32f4] Received 418 bytes from server on socket 9:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Tue, 04 Mar 2014 18:32:10 GMT

101
<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><ns:decodeResponse xmlns:ns="http://sipuui"><ns:return>867-5309</ns:return></ns:decodeResponse></soapenv:Body></soapenv:Envelope>


Troubleshooting Tip:

If you're confident you've configured things properly but are still having issues with web service calls from the strategy - check your URS and HTTP Bridge logs.  If you notice the message below in your URS log and the HTTP Bridge log is clean - you're likely encountering a Genesys bug.  I had URS 8.1.2 loaded when I was getting these errors.  I upgraded to 8.1.3 and this error magically disappeared.

09:23:18.115 Int 22000 Web Service Access Error



Sunday, March 2, 2014

SIP UUI Header Parsing in Java

Summary

This article is going to expand on the SIP UUI decoding topic I introduced in a previous post.

I'm going to discuss the development of a recursive descent parser for the SIP UUI header per the definition in the current IETF draft.  I wrote the parser in Java and implemented it as SOAP-based web service.  The motivation for this exercise is to build an end-to-end SIP UUI integration with Genesys call routing.  I'll discuss that in a later post.

Application Layout


Figure 1



Parser Implementation

That IETF draft includes a Backus-Naur Form definition of the SIP UUI header.  This definition corresponds to a context-free grammar (CFG).  A CFG consists of production rules, terminals, non-terminals, and one special non-terminal known as the 'start' symbol.  Terminals equate to base symbols such as keywords or literals.  Production rules define how non-terminals expand to terminals.  The left side of a production rule is a non-terminal.  The right side is a sequence of terminals and non-terminals. Example, using the IETF spec:

UUI -> "User-to-User" HCOLON uui-value *(COMMA uui-value)

This is a production rule.  UUI is a non-terminal and also the start symbol.  uui-value is another non-terminal in this production.  The terminals of this production are:  the "User-to-User" keyword, HCOLON (aka ':'), and COMMA (aka ',').  *(COMMA uui-value) is a regular expression that means zero or more occurrences of a comma (',') and uui-value can occur after the first uui-value.

Developing a parser for simple CFG's such as this is straightforward.  In fact, there are many tools out there to auto-generate a parser.  For this simple grammar, I wrote the parser by hand.  Fortunately, this is a quick process in Java.

I leveraged the Java built-in StringTokenizer class to create a simple lexical analyzer.  The analyzer ignores white space and breaks a SIP UUI header input up into a series of tokens.  Those tokens are then 'match'ed per the grammar's production rules.  Code snippet below:

tokenizer = new StringTokenizer(header.replaceAll("\\s", ""), ":;,\"=", true);
lookahead = tokenizer.nextToken();


....



private void match(String token) throws Exception
{
  logger.debug("Entering match(token=" + token  + ")");
  if (lookahead.equalsIgnoreCase(token))
  {
  if (tokenizer.hasMoreTokens())
  lookahead = tokenizer.nextToken();
  else
  lookahead = "\0";
  logger.debug("Exiting match()");
  }
  else
  {

    String errMsg = "Syntax Error - Expected:" + token + ", Found:" + lookahead;
  logger.error("Error in parse():" + errMsg);
  throw new Exception(errMsg);
  }

}

Each non-terminal of the grammar corresponds a Java method.  That method 'match'es terminals and/or calls further methods for non-terminals, per the production rules.  In some cases, a recursive call is made for the non-terminal hence the name 'recursive' descent parser.  Example methods below for UUI and uui-value non-terminals.  Genesys strips the 'User-to-User:' preamble, so I'm not 'match'ing it below (commented out).

private void uui() throws Exception
{
  logger.debug("Entering uui()");


  /*
  The next 2 tokens get stripped during the Genesys SIP to TLib conversion hence they're commented out so as to be ignored.
  */
  //match(USERTOUSER);
  //match(COLON);


  uuiValue();
  while (lookahead.equals(COMMA))
  {
  match(COMMA);
  uuiValue();
  }
  logger.debug("Exiting uui()");
}

With the UUI header parser in place, decoding and encoding a header to/from hex and ASCII is a simple matter.

public static String[] decode(String header)
{
  logger.debug("Entering decode(header=" + header  + ")");

  UUIParser parser = new UUIParser(header);
  String[] strValues = null;
  try
  {
  ArrayList<String> hexValues = parser.parse();
  if (hexValues.size() > 0)
  {
  strValues = new String[hexValues.size()];
  for (int i = 0; i < hexValues.size(); i++)
strValues[i] = Decoder.hexToString(hexValues.get(i));
  }
  }
  catch (Exception e)
  {
  String errMsg = "error: " + e.toString();
  logger.error("Error in decode():" + errMsg);
  }
  logger.debug("Exiting decode()");
  return strValues;
}

Next step was turning this into a web service.  Auto-generating a web service from Java code is super easy in Eclipse.  A very nice tutorial on how to do that is here.  Using the Eclipse tools, I generated the web service and client stub (for testing).

Execution

Below are sample encode and decode SOAP calls from curl and their output:

UUI encoding the string 'test'.

SOAP input envelope - soapencode.xml:
-------------------------------------------------------
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body><ns1:encode xmlns:ns1="http://sipuui"><ns1
:values>test</ns1:values></ns1:encode></soapenv:Body></soapenv:Envelope>

------------------------------------------------------

curl -v -H 'Content-Type: application/soap+xml; charset=UTF-8; action="urn:encode"' -X POST -d "@soapencode.xml" http://localhost:8080/sipuui/services/UUITranscoder.UUITranscoderHttpSoap11Endpoint/
* Adding handle: conn: 0xa54e90
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0xa54e90) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /sipuui/services/UUITranscoder.UUITranscoderHttpSoap11Endpoint/ HTTP/1.1
> User-Agent: curl/7.32.0
> Host: localhost:8080
> Accept: */*
> Content-Type: application/soap+xml; charset=UTF-8; action="urn:encode"
> Content-Length: 240
>
* upload completely sent off: 240 out of 240 bytes
< HTTP/1.1 200 OK
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Content-Type: application/soap+xml; action="urn:encodeResponse";charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 02 Mar 2014 16:44:07 GMT
<
* Connection #0 to host localhost left intact
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body><ns:encodeResponse xmlns:ns="http://sipuui"><ns:return>74657374;encoding=hex</ns:return></ns:encodeResponse></soapenv:Body></soapenv:Envelope>





Now, parsing and decoding that same header string.

SOAP input envelope - soapdecode.xml:
-------------------------------------------------------
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body><ns1:decode xmlns:ns1="http://sipuui"><ns1
:header>74657374;encoding=hex</ns1:header></ns1:decode></soapenv:Body></soapenv:Envelope>
-------------------------------------------------------


curl -v -H 'Content-Type: application/soap+xml; charset=UTF-8; action="urn:decode"' -X POST -d "@soapdecode.xml" http://localhost:8080/sipuui/services/UUITranscoder.UUITranscoderHttpSoap11Endpoint/
* Adding handle: conn: 0xf42ea0
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0xf42ea0) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /sipuui/services/UUITranscoder.UUITranscoderHttpSoap11Endpoint/ HTTP/1.1
> User-Agent: curl/7.32.0
> Host: localhost:8080
> Accept: */*
> Content-Type: application/soap+xml; charset=UTF-8; action="urn:decode"
> Content-Length: 257
>
* upload completely sent off: 257 out of 257 bytes
< HTTP/1.1 200 OK
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Content-Type: application/soap+xml; action="urn:decodeResponse";charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 02 Mar 2014 17:25:16 GMT
<
* Connection #0 to host localhost left intact
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body><ns:decodeResponse xmlns:ns="http://sipuui"><ns:return>test</ns:return></ns:decodeResponse></soapenv:Body></soapenv:Envelope>