]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Rewrote mod_osp module and updated documentation.
authorDi-Shi Sun <di-shi@transnexus.com>
Fri, 15 Mar 2013 08:32:02 +0000 (08:32 +0000)
committerDi-Shi Sun <di-shi@transnexus.com>
Fri, 15 Mar 2013 08:32:02 +0000 (08:32 +0000)
src/mod/applications/mod_osp/docs/mod_osp.txt
src/mod/applications/mod_osp/mod_osp.c

index a5686a4a0313f17e6e7dc6542e2517e87453c2f2..a12e718740bdf1a85c519fb00cb8629ae3782a26 100644 (file)
 FreeSWITCH Open Settlement Protocol (OSP) module
 
-This module provides OSP based call authentication, authorization, routing lookup and call detail record (CDR) collection services using standard FreeSWITCH application and dailplan interfaces.
+1 Introduction
+FreeSWITCH OSP module provides OSP based call authentication, authorization, routing lookup and call detail record (CDR) collection services using standard FreeSWITCH application interface and state handlers.
 
-The OSP module can be configured by the following parameters in osp.conf.xml:
+2 Configuration Parameters
+The OSP module can be configured by OSP module global parameters and OSP provider profile parameters in osp.conf.xml.
 
-Global parameters:
+2.1 Global Parameters
+FreeSWITCH OSP module global configuration parameters can be set using the following format:
 
 <settings>
-       <param name="*NAME*" value="*VALUE*"/>
+  <param name="*NAME*" value="*VALUE*"/>
 </settings>
 
 Global parameter names and values can be:
+ - debug-info: Flag to show OSP module debug information. The default is "disabled".
+ - log-level: At which log level to show OSP module debug information. The default is "info".
+ - crypto-hardware: If to use hardware for OpenSSL. The default is "disabled".
+ - sip: Used SIP module and profile. The default is "sofia" and "external".
+ - h323: Used H.323 module and profile. The default is "h323" and "external". This option has not been implemented.
+ - iax: Used IAX2 module and profile. The default is "iax" and "external". This option has not been implemented.
+ - skype: Used Skype module and profile. The default is "skypopen" and "external". This option has not been implemented.
+ - default-protocol: The VoIP protocol for destinations with unknown/undefined protocol. The default is "sip".
 
-debug-info: Flag to show OSP module debug information. The default is "disabled".
-log-level: At which log level to show OSP module debug information. The default is "info".
-crypto-hardware: If to use hardware for OpenSSL. The default is "disabled".
-sip: Used SIP module and profile. The default is "sofia" and "external".
-h323: Used H.323 module and profile. The default is "h323" and "external". This option has not been implemented.
-iax: Used IAX2 module and profile. The default is "iax" and "external". This option has not been implemented.
-skype: Used Skype module and profile. The default is "skypopen" and "external". This option has not been implemented.
-default-protocol: The VoIP protocol for destinations with unknown/undefined protocol. The default is "sip".
-
-OSP provider parameters:
+2.2 OSP Provider Parameters
+FreeSWITCH OSP module OSP provider configuration parameters can be set using the following format:
 
 <profiles>
-       <profile name="default">
-               <param name="*NAME*" value="*VALUE*"/>
-       </profile>
+  <profile name="default">
+    <param name="*NAME*" value="*VALUE*"/>
+  </profile>
 </profiles>
 
 OSP provider parameter names ane values cab be:
+ - profile: OSP provider profile name.
+ - service-point-url: OSP service point URL. This parameter must be defined. Up to 8 URLs are allowed.
+ - device-ip: FreeSWITCH IP for OSP module. This parameter must be defined.
+ - ssl-lifetime: SSL lifetime. The default is 300 in seconds.
+ - http-max-connections: HTTP max connections. The default is 20.
+ - http-persistence: HTTP persistence. The default is 60 in seconds.
+ - http-retry-delay: HTTP retry delay. The default is 0 in seconds.
+ - http-retry-limit: HTTP retry times. The default is 2.
+ - http-timeout: HTTP timeout. The default is 10000 in ms.
+ - work-mode: OSP module work mode (direct and indirect). The default is "direct".
+ - service-type: OSP service type (voice and npquery). The default is "voice".
+ - max-destinations: Max destinations OSP server will return. It is up to 12. The default is 12.
+
+3 OSP Applications
+The OSP applications are called in dial plan like this:
+
+<action application="osplookup" data="*PROFILE*>"/> and <action application="ospnext"/>
+
+*PROFILE* is an OSP service provider profile name configured in osp.conf.xml. If data attribute is not provided or its value is empty, profile name "default" is used.
+
+3.1 OSPLookup Application
+osplookup application does OSP authorization request and gets first supported destination for inbound calls. It exports a set channel variables for FreeSWITCH dial plan logic.
+osplookup application accepts two sets of channel variables that are used to pass additional inbound call information and outbound control parameters to OSP module. It also exports a set of channel variables for outbound channels and FreeSWITCH dial plan logic.
+
+3.1.1 Inbound Call Information
+ - osp_source_device: Actual source device IP address channel variable. It is only for FreeSWITH OSP module running in indirect mode.
+ - osp_source_nid: Source device network ID channel variable.
+ - osp_custom_info_N: Up to 8 custom info channel variables. N is the index starting from 1.
+
+3.1.2 Outbound Control Parameters
+ - osp_networkid_userparam: The URI user parameter name that is used to present destination network ID. For example, sip:callednumber;networkid=dnid@host.
+ - osp_networkid_uriparam: The URI parameter name that is used to present destination network ID. For example, sip:callednumber @host;networkid=dnid.
+ - osp_user_phone: Flag to add "user=phone" URI parameter. The default is "disabled".
+ - osp_outbound_proxy: Outbound proxy IP address channel variable.
+
+3.1.3 Exported Parameters
+ - osp_profile_name: Used OSP provider profile name. It will be used by ospnext application and OSP module state handlers.
+ - osp_transaction_handle: OSP transaction handle. It will be used by ospnext application and OSP module state handlers.
+ - osp_transaction_id: OSP transaction ID. It will be used by ospnext application and OSP module state handlers for log purpose.
+ - osp_lookup_status: osplookup application status. It will be used by FreeSWITCH dial plan logic. 0 for no error.
+ - osp_route_total: Total number of destinations from OSP servers. It will be used by ospnext application and OSP module state handlers.
+ - osp_route_count: Destination index starting from 1. It will be used by ospnext application and OSP module state handlers.
+ - osp_auto_route: Bridge route string. It will be used by bridge application.
+ - osp_termiation_cause: Destination termination cause. It will be used by ospnext application and OSP module state handlers.
+
+3.2 OSPNext Application
+ospnext application gets next supported destination for inbound calls. It exports a set channel variables for FreeSWITCH dial plan logic.
+ospnext application accepts a set of channel variables exported by osplookup application to pass OSP call transaction information to OSP module. It also exports a set of channel variables for outbound channels and FreeSWITCH dial plan logic.
+
+3.2.1 Transaction Parameters
+ - osp_profile_name: Used OSP provider profile name.
+ - osp_transaction_handle: OSP transaction handle.
+ - osp_transaction_id: OSP transaction ID. It is for log purpose.
+ - osp_route_total: Total number of destinations from OSP servers.
+ - osp_route_count: destination index starting from 1.
+ - osp_termiation_cause: Destination termination cause.
+
+3.2.2 Exported Parameters
+ - osp_next_status: ospnext application status. It will be used by FreeSWITCH dial plan logic. 0 for no error.
+ - osp_route_count: Destination index starting from 1. It will be used by ospnext application and OSP module state handlers.
+ - osp_termiation_cause: Destination termination cause. It will be used by ospnext application and OSP module state handlers.
+ - osp_auto_route: Bridge route string. It will be used by bridge application.
+
+4 State Handler
+OSP module state handler reports usage of calls. 
+OSP module state handler accepts a set of channel variables exported by osplookup and/or ospnext applications to pass OSP call transaction information to OSP module.
+
+4.1 Transaction Parameters
+ - osp_profile_name: Used OSP provider profile name.
+ - osp_transaction_handle: OSP transaction handle.
+ - osp_transaction_id: OSP transaction ID. It is for log purpose.
+ - osp_route_total: Total number of destinations from OSP servers.
+ - osp_route_count: destination index starting from 1.
+ - osp_termiation_cause: Destination termination cause.
+
+5 Appendix
+
+5.1 Sample Configuration
+<configuration name="osp.conf" description="OSP Module Configuration">
+  <settings>
+    <!-- Debug info flag -->
+    <param name="debug-info" value="disabled"/>
+    <!-- Log level for debug info -->
+    <param name="log-level" value="info"/>
+    <!-- Crypto hareware accelerate is disabled by default -->
+    <param name="crypto-hardware" value="disabled"/>
+    <!-- SIP settings -->
+    <param name="sip" module="sofia" profile="external"/>
+    <!-- H.323 settings -->
+    <!-- <param name="h323" module="h323" profile="external"/> -->
+    <!-- IAX settings -->
+    <!-- <param name="iax" module="iax" profile="external"/> -->
+    <!-- Skype settings -->
+    <!-- <param name="skype" module="skypopen" profile="external"/> -->
+    <!-- Default destination protocol -->
+    <param name="default-protocol" value="sip"/>
+  </settings>
+
+  <profiles>
+  <!-- Default OSP profile -->
+    <profile name="default">
+      <!-- Service point URLs, up to 8 allowed -->
+      <!-- <param name="service-point-url" value="http://osptestserver.transnexus.com:5045/osp"/> -->
+      <!-- <param name="service-point-url" value="https://127.0.0.1:1443/osp"/> -->
+      <param name="service-point-url" value="http://127.0.0.1:5045/osp"/>
+
+      <!-- FreeSWITCH IP address for OSP -->
+      <param name="device-ip" value="127.0.0.1:5080"/>
+
+      <!-- SSL lifetime in seconds -->
+      <param name="ssl-lifetime" value="300"/>
+      <!-- HTTP max connections, 1~1000 -->
+      <param name="http-max-connections" value="20"/>
+      <!-- HTTP persistence in seconds -->
+      <param name="http-persistence" value="60"/>
+      <!-- HTTP retry delay in seconds, 0~10 -->
+      <param name="http-retry-delay" value="0"/>
+      <!-- HTTP retry limit, 0~100 -->
+      <param name="http-retry-limit" value="2"/>
+      <!-- HTTP timeout in milliseconds, 200~60000 -->
+      <param name="http-timeout" value="10000"/>
+
+      <!-- OSP work mode, direct or indirect -->
+      <param name="work-mode" value="direct"/>
+      <!-- OSP service type, voice or npquery -->
+      <param name="service-type" value="voice"/>
+      <!-- Max number of destinations -->
+      <param name="max-destinations" value="12"/>
+    </profile>
+  </profiles>
+</configuration>
+
+5.2 Sample Dialplan
+<include>
+  <context name="txnx">
+    <extension name="unloop">
+      <condition field="${unroll_loops}" expression="^true$"/>
+      <condition field="${sip_looped_call}" expression="^true$">
+        <action application="deflect" data="${destination_number}"/>
+      </condition>
+    </extension>
+
+    <!--
+        Tag anything pass thru here as an outside_call so you can make sure not
+        to create any routing loops based on the conditions that it came from 
+        the outside of the switch.  
+    -->
+
+    <extension name="outside_call" continue="true">
+      <condition>
+        <action application="set" data="outside_call=true"/>
+        <action application="export" data="RFC2822_DATE=${strftime(%a, %d %b %Y %T %z)}"/>
+      </condition>
+    </extension>
+
+    <extension name="call_debug" continue="true">
+      <condition field="${call_debug}" expression="^true$" break="never">
+        <action application="info"/>
+      </condition>
+    </extension>
+
+    <!-- XML dialplan for OSP -->
+    <extension name="osp">
+      <!-- For any called number -->
+      <condition field="destination_number" expression="^(.*)$">
+        <!-- Jump to OSP logic with destination_number as called number -->
+        <action application="transfer" data="$1 XML osp_lookup"/>
+      </condition>
+    </extension>
+  </context>
+
+  <!-- OSP lookup application logic -->
+  <context name="osp_lookup">
+    <extension name="lookup">
+      <condition>
+        <!-- Inbound information parameters -->
+        <!-- Actual source device -->
+        <action application="set" data="osp_source_device=${sip_h_P-Source-Device}"/>
+        <!-- Source network ID -->
+        <action application="set" data="osp_source_nid=${sip_h_P-Network-ID}"/>
+        <!-- Custom info -->
+        <action application="set" data="osp_custom_info_4=${sip_h_P-Custom-Info}"/>
+        <!-- Outbound control parameters -->
+        <!-- Destination network ID parameter name, "sip:callednumber;dnidname=dnidvalue@host" -->
+        <!-- <action application="set" data="osp_networkid_userparam=networkid"/> -->
+        <!-- Destination network ID parameter name, "sip:callednumber@host;dnidname=dnidvalue" -->
+        <action application="set" data="osp_networkid_uriparam=networkid"/>
+        <!-- Append "user=phone" -->
+        <!-- <action application="set" data="osp_user_phone=enabled"/> -->
+        <!-- Outbound proxy -->
+        <!-- <action application="set" data="osp_outbound_proxy=${network_addr}"/> -->
+        <!-- OSP lookup application with default OSP profile -->
+        <action application="osplookup" data="default"/>
+        <!-- Debug info -->
+        <!-- OSP profile name -->
+        <action application="log" data="DEBUG osp_profile_name = ${osp_profile_name}"/>
+        <!-- OSP transaction handle -->
+        <action application="log" data="DEBUG osp_transaction_handle = ${osp_transaction_handle}"/>
+        <!-- OSP transaction ID -->
+        <action application="log" data="DEBUG osp_transaction_id = ${osp_transaction_id}"/>
+        <!-- OSP lookup application status -->
+        <action application="log" data="DEBUG osp_lookup_status = ${osp_lookup_status}"/>
+        <!-- Total number of destinations -->
+        <action application="log" data="DEBUG osp_route_total = ${osp_route_total}"/>
+        <!-- Count of current destination, starting from 1 -->
+        <action application="log" data="DEBUG osp_route_count = ${osp_route_count}"/>
+        <!-- Bridge application dial string -->
+        <action application="log" data="DEBUG osp_auto_route = ${osp_auto_route}"/>
+        <!-- Jump out to check OSP Lookup status -->
+        <action application="transfer" data="lookup XML osp_check_lookup"/>
+      </condition>
+    </extension>
+  </context>
+
+  <!-- Check OSP lookup application status -->
+  <context name="osp_check_lookup">
+    <extension name="lookup">
+      <condition field="${osp_lookup_status}" expression="0">
+        <!-- OSP Lookup status success, jump to dail -->
+        <action application="transfer" data="dial XML osp_dial"/>
+        <!-- OSP Lookup status false, jump to done -->
+        <anti-action application="transfer" data="done XML osp_done"/>
+      </condition>
+    </extension>
+  </context>
+
+  <!-- Dail destination -->
+  <context name="osp_dial">
+    <extension name="dial">
+      <condition>
+        <!-- Bridge control parameters -->
+        <action application="set" data="continue_on_fail=true"/>
+        <action application="set" data="hangup_after_bridge=false"/>
+        <!-- Bridge call legs -->
+        <action application="bridge" data="${osp_auto_route}"/>
+        <!-- Debug info -->
+        <!-- Destination termination cause for failed call -->
+        <action application="log" data="DEBUG last_bridge_hangup_cause = ${last_bridge_hangup_cause}"/>
+        <!-- Jump out to run OSP Next -->
+        <action application="transfer" data="next XML osp_next"/>
+      </condition>
+    </extension>
+  </context>
+
+  <!-- OSP next application logic -->
+  <context name="osp_next">
+    <extension name="next">
+      <condition>
+        <!-- OSP next application -->
+        <action application="ospnext"/>
+        <!-- Debug info -->
+        <!-- OSP next application status -->
+        <action application="log" data="DEBUG osp_next_status = ${osp_next_status}"/>
+        <!-- Count of current destination, starting from 1 -->
+        <action application="log" data="DEBUG osp_route_count = ${osp_route_count}"/>
+        <!-- Bridge application dial string -->
+        <action application="log" data="DEBUG osp_auto_route = ${osp_auto_route}"/>
+        <!-- Jump out to check OSP Next application status -->
+        <action application="transfer" data="next XML osp_check_next"/>
+      </condition>
+    </extension>
+  </context>
+
+  <!-- Check OSP next application status -->
+  <context name="osp_check_next">
+    <extension name="next">
+      <condition field="${osp_next_status}" expression="0">
+        <!-- OSP Next status success, jump to dail -->
+        <action application="transfer" data="dial XML osp_dial"/>
+        <!-- OSP Next status false, jump to done -->
+        <anti-action application="transfer" data="done XML osp_done"/>
+      </condition>
+    </extension>
+  </context>
 
-profile: OSP provider profile name.
-service-point-url: OSP service point URL. This parameter must be defined. Up to 8 URLs are allowed.
-device-ip: FreeSWITCH IP for OSP module. This parameter must be defined.
-ssl-lifetime: SSL lifetime. The default is 300 in seconds.
-http-max-connections: HTTP max connections. The default is 20.
-http-persistence: HTTP persistence. The default is 60 in seconds.
-http-retry-delay: HTTP retry delay. The default is 0 in seconds.
-http-retry-limit: HTTP retry times. The default is 2.
-http-timeout: HTTP timeout. The default is 10000 in ms.
-work-mode: OSP module work mode (direct and indirect). The default is "direct".
-service-type: OSP service type (voice and npquery). The default is "voice".
-max-destinations: Max destinations OSP server will return. It is up to 12. The default is 12.
-
-The OSP application is called in dial plan like this:
-
-<action application="osp" data="<profilename>"/>
-
-The OSP dialplan is called in dial plan like this:
-
-<param name="dialplan" value="osp:<profilename>"/>
-
-For both OSP application and dialplan, the <profilename> is an OSP service provider name configured in osp.conf.xml. If it is empty, profile "default" is used.
-
-Both OSP application and dialplan accept a set of inbound channel variables that are used to pass additional call information to OSP module. These channel variables include:
-
-osp_source_device: Actual source device IP address channel variable. It is only for FreeSWITH OSP module running in indirect mode.
-osp_source_nid: Source device network ID channel variable.
-osp_custom_info_N: Up to 8 custom info channel variables. N is the index starting from 1.
-osp_networkid_userparam: The URI user parameter name that is used to present destination network ID.
-osp_networkid_uriparam: The URI parameter name that is used to present destination network ID.
-osp_user_phone: Flag to add "user=phone" URI parameter. The default is "disabled".
-osp_outbound_proxy: Outbound proxy IP address channel variable.
-
-Both OSP application and dialplan also export a set of channel variables for outbound channels and FreeSWITCH dial plan logic (for OSP dialplan, some exported channel variables are not visible for dial plan). These channel variables include:
-
-osp_profile: Used OSP profile name. Used by outbound channels.
-osp_transaction_id: OSP transaction ID. Used by outbound channels.
-osp_calling: Original inbound calling number. Used by outbound channels.
-osp_called: Original inbound called number. Used by outbound channels.
-osp_start_time: Inbound call start time. Used by outbound channels.
-osp_source_device: Actual source device. Used by outbound channels. It is only for FreeSWITH OSP module running in indirect mode.
-osp_source_nid: Source network ID. Used by outbound channels.
-osp_destination_total: Total number of destinations from OSP servers. Used by outbound channels.
-osp_destination_count: Destination index. Used by outbound channels.
-osp_destination_ip: Destination IP. Used by outbound channels.
-osp_destination_nid: Destination network ID. Used by outbound channels.
-osp_authreq_status: Authorization request result status.
-osp_route_count: Number of supported destinations.
-osp_route_N: Destination route string. N is the index starting from 1.
-osp_auto_route: Bridge route string.
+  <!-- OSP logic for failed calls -->
+  <context name="osp_done">
+    <extension name="done">
+      <condition>
+        <!-- Respond 503 -->
+        <action application="respond" data="503 Service unavailable"/>
+        <action application="hangup" data="NORMAL_TEMPORARY_FAILURE"/>
+      </condition>
+    </extension>
+  </context>
+</include>
 
index 6f312386f4dce4aedc16ca87219c342e3e9e057c..52ccf7d2b18237c343f12f1d441a408bfe858e6e 100644 (file)
 #include <osp/osptrans.h>
 
 /* OSP Buffer Size Constants */
-#define OSP_SIZE_NORSTR                256             /* OSP normal string buffer size */
+#define OSP_SIZE_NORSTR                512             /* OSP normal string buffer size */
 #define OSP_SIZE_KEYSTR                1024    /* OSP certificate string buffer size */
-#define OSP_SIZE_ROUSTR                4096    /* OSP route buffer size */
-#define OSP_SIZE_TOKSTR                4096    /* OSP token string buffer size */
+#define OSP_SIZE_ROUSTR                1024    /* OSP route buffer size */
 
-/* OSP Settings Constants */
-#define OSP_MAX_SP                     8                                       /* Max number of OSP service points */
-#define OSP_AUDIT_URL          "localhost"                     /* OSP default Audit URL */
-#define OSP_LOCAL_VALID                1                                       /* OSP token validating method, locally */
+/* OSP Module Configuration Constants */
+#define OSP_CONFIG_FILE                "osp.conf"                      /* OSP module configuration file name */
+#define OSP_DEF_PROFILE                "default"                       /* Default OSP profile name */
+#define OSP_MAX_SPNUMBER       8                                       /* Max number of OSP service points */
 #define OSP_DEF_LIFETIME       300                                     /* OSP default SSL lifetime */
 #define OSP_MIN_MAXCONN                1                                       /* OSP min max connections */
 #define OSP_MAX_MAXCONN                1000                            /* OSP max max connections */
 #define OSP_MIN_TIMEOUT                200                                     /* OSP min timeout in ms */
 #define OSP_MAX_TIMEOUT                60000                           /* OSP max timeout in ms */
 #define OSP_DEF_TIMEOUT                10000                           /* OSP default timeout in ms */
-#define OSP_CUSTOMER_ID                ""                                      /* OSP customer ID */
-#define OSP_DEVICE_ID          ""                                      /* OSP device ID */
 #define OSP_MIN_MAXDEST                1                                       /* OSP min max destinations */
 #define OSP_MAX_MAXDEST                12                                      /* OSP max max destinations */
 #define OSP_DEF_MAXDEST                OSP_MAX_MAXDEST         /* OSP default max destinations */
-#define OSP_DEF_PROFILE                "default"                       /* OSP default profile name */
-#define OSP_DEF_STRING         ""                                      /* OSP default empty string */
-#define OSP_DEF_CALLID         "UNDEFINED"                     /* OSP default Call-ID */
-#define OSP_DEF_STATS          -1                                      /* OSP default statistics */
-#define OSP_URI_DELIM          '@'                                     /* URI delimit */
-#define OSP_USER_DELIM         ";:"                            /* URI userinfo delimit */
-#define OSP_HOST_DELIM         ";>"                            /* URI hostport delimit */
-#define OSP_MAX_CINFO          8                                       /* Max number of custom info */
 
 /* OSP Handle Constant */
 #define OSP_INVALID_HANDLE     -1      /* Invalid OSP handle, provider, transaction etc. */
 
+/* OSP Provider Contants */
+#define OSP_AUDIT_URL          "localhost"     /* OSP default Audit URL */
+#define OSP_LOCAL_VALID                1                       /* OSP token validating method, locally */
+#define OSP_CUSTOMER_ID                ""                      /* OSP customer ID */
+#define OSP_DEVICE_ID          ""                      /* OSP device ID */
+
+/* URI Contants */
+#define OSP_URI_DELIM          '@'             /* URI delimit */
+#define OSP_USER_DELIM         ";:"    /* URI userinfo delimit */
+#define OSP_HOST_DELIM         ";>"    /* URI hostport delimit */
+
+/* OSP Module Other Contants */
+#define OSP_MAX_CINFO          8       /* Max number of custom info */
+#define OSP_DEF_STRING         ""      /* OSP default empty string */
+#define OSP_DEF_STATS          -1      /* OSP default statistics */
+
 /* OSP Supported Signaling Protocols for Default Protocol */
 #define OSP_PROTOCOL_SIP       "sip"                   /* SIP protocol name */
 #define OSP_PROTOCOL_H323      "h323"                  /* H.323 protocol name */
 #define OSP_MODULE_IAX         "mod_iax"               /* FreeSWITCH IAX module name */
 #define OSP_MODULE_SKYPE       "mod_skypopen"  /* FreeSWITCH Skype module name */
 
-/* OSP Variables Name */
-#define OSP_VAR_PROFILE                        "osp_profile"                           /* Profile name, in cookie */
-#define OSP_VAR_TRANSID                        "osp_transaction_id"            /* Transaction ID, in cookie */
-#define OSP_VAR_CALLING                        "osp_calling"                           /* Original calling number, in cookie */
-#define OSP_VAR_CALLED                 "osp_called"                            /* Original called number, in cookie */
-#define OSP_VAR_START                  "osp_start_time"                        /* Inbound Call start time, in cookie */
-#define OSP_VAR_SRCDEV                 "osp_source_device"                     /* Source device IP, in cookie or inbound (actual source device)*/
-#define OSP_VAR_SRCNID                 "osp_source_nid"                        /* Source network ID, inbound and in cookie */
-#define OSP_VAR_DESTTOTAL              "osp_destination_total"         /* Total number of destinations in AuthRsp, in cookie */
-#define OSP_VAR_DESTCOUNT              "osp_destination_count"         /* Destination count, in cookie */
-#define OSP_VAR_DESTIP                 "osp_destination_ip"            /* Destination IP, in cookie */
-#define OSP_VAR_DESTNID                        "osp_destination_nid"           /* Destination network ID, in cookie */
-#define OSP_VAR_CUSTOMINFO             "osp_custom_info_"                      /* Custom info */
-#define OSP_VAR_DNIDUSERPARAM  "osp_networkid_userparam"       /* Destination network ID user parameter name */
-#define OSP_VAR_DNIDURIPARAM   "osp_networkid_uriparam"        /* Destination network ID URI parameter name */
-#define OSP_VAR_USERPHONE              "osp_user_phone"                        /* If to add "user=phone" */
-#define OSP_VAR_OUTPROXY               "osp_outbound_proxy"            /* Outbound proxy */
-#define OSP_VAR_AUTHSTATUS             "osp_authreq_status"            /* AuthReq Status */
-#define OSP_VAR_ROUTECOUNT             "osp_route_count"                       /* Number of destinations */
-#define OSP_VAR_ROUTEPRE               "osp_route_"                            /* Destination prefix */
+/* OSP Variable Names */
+#define OSP_VAR_SRCDEV                 "osp_source_device"                     /* Source device IP, inbound (actual source device)*/
+#define OSP_VAR_SRCNID                 "osp_source_nid"                        /* Source network ID, inbound */
+#define OSP_VAR_CUSTOMINFO             "osp_custom_info_"                      /* Custom info, inbound */
+#define OSP_VAR_DNIDUSERPARAM  "osp_networkid_userparam"       /* Destination network ID user parameter name, outbound */
+#define OSP_VAR_DNIDURIPARAM   "osp_networkid_uriparam"        /* Destination network ID URI parameter name, outbound */
+#define OSP_VAR_USERPHONE              "osp_user_phone"                        /* If to add "user=phone", outbound */
+#define OSP_VAR_OUTPROXY               "osp_outbound_proxy"            /* Outbound proxy, outbound */
+#define OSP_VAR_PROFILE                        "osp_profile_name"                      /* Profile name */
+#define OSP_VAR_TRANSACTION            "osp_transaction_handle"        /* Transaction handle */
+#define OSP_VAR_TRANSID                        "osp_transaction_id"            /* Transaction ID */
+#define OSP_VAR_ROUTETOTAL             "osp_route_total"                       /* Total number of destinations */
+#define OSP_VAR_ROUTECOUNT             "osp_route_count"                       /* Destination count */
+#define OSP_VAR_TCCODE                 "osp_termination_cause"         /* Terimation cause */
 #define OSP_VAR_AUTOROUTE              "osp_auto_route"                        /* Bridge route string */
+#define OSP_VAR_LOOKUPSTATUS   "osp_lookup_status"                     /* OSP lookup function status */
+#define OSP_VAR_NEXTSTATUS             "osp_next_status"                       /* OSP next function status */
 
-/* OSP Use Variable Name */
+/* OSP Using FreeSWITCH Variable Names */
+#define OSP_FS_CALLID                  "sip_call_id"                                           /* Inbound SIP Call-ID */
 #define OSP_FS_FROMUSER                        "sip_from_user"                                         /* Inbound SIP From user */
 #define OSP_FS_TOHOST                  "sip_to_host"                                           /* Inbound SIP To host */
 #define OSP_FS_TOPORT                  "sip_to_port"                                           /* Inbound SIP To port */
 #define OSP_FS_PAI                             "sip_P-Asserted-Identity"                       /* Inbound SIP P-Asserted-Identity header */
 #define OSP_FS_DIV                             "sip_h_Diversion"                                       /* Inbound SIP Diversion header */
 #define OSP_FS_PCI                             "sip_h_P-Charge-Info"                           /* Inbound SIP P-Charge-Info header */
-#define OSP_FS_OUTCALLID               "sip_call_id"                                           /* Outbound SIP Call-ID */
 #define OSP_FS_OUTCALLING              "origination_caller_id_number"          /* Outbound calling number */
-#define OSP_FS_SIPRELEASE              "sip_hangup_disposition"                        /* SIP release source */
-#define OSP_FS_SRCCODEC                        "write_codec"                                           /* Source codec */
-#define OSP_FS_DESTCODEC               "read_codec"                                            /* Destiantion codec */
-#define OSP_FS_RTPSRCREPOCTS   "rtp_audio_out_media_bytes"                     /* Source->reporter octets */
-#define OSP_FS_RTPDESTREPOCTS  "rtp_audio_in_media_bytes"                      /* Destination->reporter octets */
-#define OSP_FS_RTPSRCREPPKTS   "rtp_audio_out_media_packet_count"      /* Source->reporter packets */
-#define OSP_FS_RTPDESTREPPKTS  "rtp_audio_in_media_packet_count"       /* Destination->reporter packets */
-
-typedef struct osp_settings {
-       switch_bool_t debug;                                            /* OSP module debug info flag */
-       switch_log_level_t loglevel;                            /* Log level for debug info */
-       switch_bool_t hardware;                                         /* Crypto hardware flag */
-       const char *modules[OSPC_PROTNAME_NUMBER];      /* Endpoint names */
-       const char *profiles[OSPC_PROTNAME_NUMBER];     /* Endpoint profile names */
-       OSPE_PROTOCOL_NAME protocol;                            /* Default signaling protocol */
-       switch_bool_t shutdown;                                         /* OSP module status */
-       switch_memory_pool_t *pool;                                     /* OSP module memory pool */
-} osp_settings_t;
+#define OSP_FS_SIPRELEASE              "sip_hangup_disposition"                        /* Usage SIP release source */
+#define OSP_FS_SRCCODEC                        "write_codec"                                           /* Usage source codec */
+#define OSP_FS_DESTCODEC               "read_codec"                                            /* Usage destiantion codec */
+#define OSP_FS_RTPSRCREPOCTS   "rtp_audio_out_media_bytes"                     /* Usage source->reporter octets */
+#define OSP_FS_RTPDESTREPOCTS  "rtp_audio_in_media_bytes"                      /* Usage destination->reporter octets */
+#define OSP_FS_RTPSRCREPPKTS   "rtp_audio_out_media_packet_count"      /* Usage source->reporter packets */
+#define OSP_FS_RTPDESTREPPKTS  "rtp_audio_in_media_packet_count"       /* Usage destination->reporter packets */
+#define OSP_FS_HANGUPCAUSE             "last_bridge_hangup_cause"                      /* Termination cause */
+
+/* FreeSWITCH Endpoint Parameters */
+typedef struct osp_endpoint {
+       const char *module;             /* Endpoint module name */
+       const char *profile;    /* Endpoint profile name */
+} osp_endpoint_t;
+
+/* OSP Global Status */
+typedef struct osp_global {
+       switch_bool_t debug;                                                    /* OSP module debug flag */
+       switch_log_level_t loglevel;                                    /* Log level for debug messages */
+       switch_bool_t hardware;                                                 /* Crypto hardware flag */
+       osp_endpoint_t endpoint[OSPC_PROTNAME_NUMBER];  /* Used endpoints */
+       OSPE_PROTOCOL_NAME protocol;                                    /* Default signaling protocol */
+       switch_bool_t shutdown;                                                 /* OSP module status */
+       switch_memory_pool_t *pool;                                             /* OSP module memory pool */
+} osp_global_t;
 
 /* OSP Work Modes */
 typedef enum osp_workmode {
@@ -153,28 +161,32 @@ typedef enum osp_srvtype {
        OSP_SRV_NPQUERY         /* Number portability query service */
 } osp_srvtype_t;
 
+/* OSP Profile Parameters */
 typedef struct osp_profile {
-       const char *name;                               /* OSP profile name */
-       int spnum;                                              /* Number of OSP service points */
-       const char *spurls[OSP_MAX_SP]; /* OSP service point URLs */
-       const char *device;                             /* OSP source IP */
-       int lifetime;                                   /* SSL life time */
-       int maxconnect;                                 /* Max number of HTTP connections */
-       int persistence;                                /* HTTP persistence in seconds */
-       int retrydelay;                                 /* HTTP retry delay in seconds */
-       int retrylimit;                                 /* HTTP retry times */
-       int timeout;                                    /* HTTP timeout in ms */
-       osp_workmode_t workmode;                /* OSP work mode */
-       osp_srvtype_t srvtype;                  /* OSP service type */
-       int maxdest;                                    /* Max destinations */
-       OSPTPROVHANDLE provider;                /* OSP provider handle */
-       struct osp_profile *next;               /* Next OSP profile */
+       const char *name;                                               /* OSP profile name */
+       int spnumber;                                                   /* Number of OSP service points */
+       const char *spurl[OSP_MAX_SPNUMBER];    /* OSP service point URLs */
+       const char *deviceip;                                   /* OSP client end IP */
+       int lifetime;                                                   /* SSL life time */
+       int maxconnect;                                                 /* Max number of HTTP connections */
+       int persistence;                                                /* HTTP persistence in seconds */
+       int retrydelay;                                                 /* HTTP retry delay in seconds */
+       int retrylimit;                                                 /* HTTP retry times */
+       int timeout;                                                    /* HTTP timeout in ms */
+       osp_workmode_t workmode;                                /* OSP work mode */
+       osp_srvtype_t srvtype;                                  /* OSP service type */
+       int maxdest;                                                    /* Max destinations */
+       OSPTPROVHANDLE provider;                                /* OSP provider handle */
+       struct osp_profile *next;                               /* Next OSP profile */
 } osp_profile_t;
 
+/* OSP Inbound Parameters */
 typedef struct osp_inbound {
        const char *actsrc;                                     /* Actual source device IP address */
        const char *srcdev;                                     /* Source device IP address */
+       const char *srcnid;                                     /* Source network ID */
        OSPE_PROTOCOL_NAME protocol;            /* Inbound signaling protocol */
+       const char *callid;                                     /* Inbound Call-ID */
        char calling[OSP_SIZE_NORSTR];          /* Inbound calling number */
        char called[OSP_SIZE_NORSTR];           /* Inbound called number */
        char nprn[OSP_SIZE_NORSTR];                     /* Inbound NP routing number */
@@ -187,16 +199,20 @@ typedef struct osp_inbound {
        char divuser[OSP_SIZE_NORSTR];          /* Inbound user of SIP Diversion header */
        char divhost[OSP_SIZE_NORSTR];          /* Inbound hostport of SIP Diversion header */
        char pciuser[OSP_SIZE_NORSTR];          /* Inbound user of SIP P-Charge-Info header */
-       const char *srcnid;                                     /* Inbound source network ID */
-       switch_time_t start;                            /* Call start time */
        const char *cinfo[OSP_MAX_CINFO];       /* Custom info */
 } osp_inbound_t;
 
-typedef struct osp_destination {
+/* OSP Route Parameters */
+typedef struct osp_results {
+       const char *profile;                                                            /* Profile name */
+       OSPTTRANHANDLE transaction;                                                     /* Transaction handle */
+       uint64_t transid;                                                                       /* Transaction ID */
+       unsigned int total;                                                                     /* Total number of destinations */
+       unsigned int count;                                                                     /* Destination count starting from 1 */
        unsigned int timelimit;                                                         /* Outbound duration limit */
-       char dest[OSP_SIZE_NORSTR];                                                     /* Destination IP address */
        char calling[OSP_SIZE_NORSTR];                                          /* Outbound calling number, may be translated */
        char called[OSP_SIZE_NORSTR];                                           /* Outbound called number, may be translated */
+       char dest[OSP_SIZE_NORSTR];                                                     /* Destination IP address */
        char destnid[OSP_SIZE_NORSTR];                                          /* Destination network ID */
        char nprn[OSP_SIZE_NORSTR];                                                     /* Outbound NP routing number */
        char npcic[OSP_SIZE_NORSTR];                                            /* Outbound NP carrier identification code */
@@ -204,35 +220,10 @@ typedef struct osp_destination {
        char opname[OSPC_OPNAME_NUMBER][OSP_SIZE_NORSTR];       /* Outbound Operator names */
        OSPE_PROTOCOL_NAME protocol;                                            /* Signaling protocol */
        switch_bool_t supported;                                                        /* Supported by FreeRADIUS OSP module */
-} osp_destination_t;
-
-typedef struct osp_results {
-       const char *profile;                                            /* Profile name */
-       uint64_t transid;                                                       /* Transaction ID */
-       switch_time_t start;                                            /* Call start time */
-       char calling[OSP_SIZE_NORSTR];                          /* Original calling number */
-       char called[OSP_SIZE_NORSTR];                           /* Original called number */
-       const char *srcdev;                                                     /* Source device IP */
-       const char *srcnid;                                                     /* Source network ID */
-       int status;                                                                     /* AuthReq status */
-       int numdest;                                                            /* Number of destinations */
-       osp_destination_t dests[OSP_MAX_MAXDEST];       /* Destinations */
+       switch_call_cause_t cause;                                                      /* Termination cause for current destination */
 } osp_results_t;
 
-typedef struct osp_cookie {
-       const char *profile;    /* Profile name */
-       uint64_t transid;               /* Transaction ID */
-       const char *calling;    /* Original calling number */
-       const char *called;             /* Original called number */
-       switch_time_t start;    /* Call start time */
-       const char *srcdev;             /* Source Device IP */
-       int desttotal;                  /* Total number of destinations in AuthRsp */
-       int destcount;                  /* Destination count */
-       const char *dest;               /* Destination IP */
-       const char *srcnid;             /* Source network ID */
-       const char *destnid;    /* Destination network ID */
-} osp_cookie_t;
-
+/* OSP Outbound Parameters */
 typedef struct osp_outbound {
        const char *dniduserparam;      /* Destination network ID user parameter name */
        const char *dniduriparam;       /* Destination network ID URI parameter name */
@@ -240,55 +231,46 @@ typedef struct osp_outbound {
        const char *outproxy;           /* Outbound proxy IP address */
 } osp_outbound_t;
 
+/* OSP Usage Parameters */
 typedef struct osp_usage {
-       const char *srcdev;                             /* Source device IP */
-       const char *callid;                             /* Call-ID */
-       OSPE_PROTOCOL_NAME inprotocol;  /* Inbound signaling protocol */
-       OSPE_PROTOCOL_NAME outprotocol; /* Outbound signaling protocol */
-       int release;                                    /* Release source */
-       switch_call_cause_t cause;              /* Termination cause */
-       switch_time_t alert;                    /* Call alert time */
-       switch_time_t connect;                  /* Call answer time */
-       switch_time_t end;                              /* Call end time */
-       switch_time_t duration;                 /* Call duration */
-       switch_time_t pdd;                              /* Post dial delay, in us */
-       const char *srccodec;                   /* Source codec */
-       const char *destcodec;                  /* Destination codec */
-       int rtpsrcrepoctets;                    /* RTP source->reporter bytes */
-       int rtpdestrepoctets;                   /* RTP destination->reporter bytes */
-       int rtpsrcreppackets;                   /* RTP source->reporter packets */
-       int rtpdestreppackets;                  /* RTP destiantion->reporter packets */
+       OSPE_RELEASE release;           /* Release source */
+       switch_call_cause_t cause;      /* Termination cause */
+       switch_time_t start;            /* Call start time */
+       switch_time_t alert;            /* Call alert time */
+       switch_time_t connect;          /* Call answer time */
+       switch_time_t end;                      /* Call end time */
+       switch_time_t duration;         /* Call duration */
+       switch_time_t pdd;                      /* Post dial delay, in us */
+       const char *srccodec;           /* Source codec */
+       const char *destcodec;          /* Destination codec */
+       int rtpsrcrepoctets;            /* RTP source->reporter bytes */
+       int rtpdestrepoctets;           /* RTP destination->reporter bytes */
+       int rtpsrcreppackets;           /* RTP source->reporter packets */
+       int rtpdestreppackets;          /* RTP destiantion->reporter packets */
 } osp_usage_t;
 
-typedef struct osp_threadarg {
-       OSPTTRANHANDLE transaction;     /* Transaction handle */
-       uint64_t transid;                       /* Transaction ID */
-       switch_call_cause_t cause;      /* Release code */
-       time_t start;                           /* Call start time */
-       time_t alert;                           /* Call alert time */
-       time_t connect;                         /* Call connect time */
-       time_t end;                                     /* Call end time */
-       int duration;                           /* Call duration */
-       int pdd;                                        /* Post dial delay, in ms */
-       int release;                            /* EP that released the call */
-} osp_threadarg_t;
-
-/* OSP module global settings */
-static osp_settings_t osp_globals;
+/* Macro functions for debug */
+#define OSP_DEBUG(_fmt, ...)                           if (osp_global.debug) { switch_log_printf(SWITCH_CHANNEL_LOG, osp_global.loglevel, "%s: "_fmt"\n", __SWITCH_FUNC__, __VA_ARGS__); }
+#define OSP_DEBUG_MSG(_msg)                                    OSP_DEBUG("%s", _msg)
+#define OSP_DEBUG_START                                                OSP_DEBUG_MSG("Start")
+#define OSP_DEBUG_END                                          OSP_DEBUG_MSG("End")
+/* Macro to prevent NULL string */
+#define OSP_FILTER_NULLSTR(_str)                       (switch_strlen_zero(_str) ? OSP_DEF_STRING : (_str))
+/* Macro to prevent NULL integer */
+#define OSP_FILTER_NULLINT(_int)                       ((_int) ? *(_int) : 0)
+/* Macro to adjust buffer length */
+#define OSP_ADJUST_LEN(_head, _size, _len)     { (_len) = strlen(_head); (_head) += (_len); (_size) -= (_len); }
+
+/* OSP Module Global Status */
+static osp_global_t osp_global;
 
 /* OSP module profiles */
 static osp_profile_t *osp_profiles = NULL;
 
-/* switch_status_t mod_osp_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)  */
-SWITCH_MODULE_LOAD_FUNCTION(mod_osp_load);
-/* switch_status_t mod_osp_shutdown(void) */
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_osp_shutdown);
-/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) */
-SWITCH_MODULE_DEFINITION(mod_osp, mod_osp_load, mod_osp_shutdown, NULL);
-
-/* Macro to prevent NULL string */
-#define osp_filter_null(_str)                          switch_strlen_zero(_str) ? OSP_DEF_STRING : _str
-#define osp_adjust_len(_head, _size, _len)     { _len = strlen(_head); _head += _len; _size -= _len; }
+/* OSP default certificates */
+static const char *B64PKey = "MIIBOgIBAAJBAK8t5l+PUbTC4lvwlNxV5lpl+2dwSZGW46dowTe6y133XyVEwNiiRma2YNk3xKs/TJ3Wl9Wpns2SYEAJsFfSTukCAwEAAQJAPz13vCm2GmZ8Zyp74usTxLCqSJZNyMRLHQWBM0g44Iuy4wE3vpi7Wq+xYuSOH2mu4OddnxswCP4QhaXVQavTAQIhAOBVCKXtppEw9UaOBL4vW0Ed/6EA/1D8hDW6St0h7EXJAiEAx+iRmZKhJD6VT84dtX5ZYNVk3j3dAcIOovpzUj9a0CECIEduTCapmZQ5xqAEsLXuVlxRtQgLTUD4ZxDElPn8x0MhAiBE2HlcND0+qDbvtwJQQOUzDgqg5xk3w8capboVdzAlQQIhAMC+lDL7+gDYkNAft5Mu+NObJmQs4Cr+DkDFsKqoxqrm";
+static const char *B64LCert = "MIIBeTCCASMCEHqkOHVRRWr+1COq3CR/xsowDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTA1MDYyMzAwMjkxOFoXDTA2MDYyNDAwMjkxOFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCvLeZfj1G0wuJb8JTcVeZaZftncEmRluOnaME3ustd918lRMDYokZmtmDZN8SrP0yd1pfVqZ7NkmBACbBX0k7pAgMBAAEwDQYJKoZIhvcNAQEEBQADQQDnV8QNFVVJx/+7IselU0wsepqMurivXZzuxOmTEmTVDzCJx1xhA8jd3vGAj7XDIYiPub1PV23eY5a2ARJuw5w9";
+static const char *B64CACert = "MIIBYDCCAQoCAQEwDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTAyMDIwNDE4MjU1MloXDTEyMDIwMzE4MjU1MlowOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPGeGwV41EIhX0jEDFLRXQhDEr50OUQPq+f55VwQd0TQNts06BP29+UiNdRW3c3IRHdZcJdC1Cg68ME9cgeq0h8CAwEAATANBgkqhkiG9w0BAQQFAANBAGkzBSj1EnnmUxbaiG1N4xjIuLAWydun7o3bFk2tV8dBIhnuh445obYyk1EnQ27kI7eACCILBZqi2MHDOIMnoN0=";
 
 /*
  * Find OSP profile by name
@@ -303,6 +285,8 @@ static switch_status_t osp_find_profile(
        osp_profile_t *p;
        switch_status_t status = SWITCH_STATUS_FALSE;
 
+       OSP_DEBUG_START;
+
        if (name) {
                if (profile) {
                        *profile = NULL;
@@ -319,6 +303,14 @@ static switch_status_t osp_find_profile(
                }
        }
 
+       if (status == SWITCH_STATUS_SUCCESS) {
+               OSP_DEBUG("Found profile '%s'", name);
+       } else {
+               OSP_DEBUG("Unable to find profile '%s'", name);
+       }
+
+       OSP_DEBUG_END;
+
        return status;
 }
 
@@ -327,11 +319,10 @@ static switch_status_t osp_find_profile(
  * param pool OSP module memory pool
  * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed, SWITCH_STATUS_MEMERR Memory Error.
  */
-static switch_status_t osp_load_settings(
+static switch_status_t osp_load_config(
        switch_memory_pool_t *pool)
 {
-       char *cf = "osp.conf";
-       switch_xml_t cfg, xml = NULL, param, settings, xprofile, profiles;
+       switch_xml_t xcfg, xml = NULL, xsettings, xparam, xprofile, xprofiles;
        const char *name;
        const char *value;
        const char *module;
@@ -340,128 +331,162 @@ static switch_status_t osp_load_settings(
        int number;
        switch_status_t status = SWITCH_STATUS_SUCCESS;
 
-       if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open '%s'\n", cf);
-               status = SWITCH_STATUS_FALSE;
-               return status;
+       OSP_DEBUG_START;
+
+       /* Load OSP module configuration file */
+       if (!(xml = switch_xml_open_cfg(OSP_CONFIG_FILE, &xcfg, NULL))) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open OSP module configuration file '%s'\n", OSP_CONFIG_FILE);
+               OSP_DEBUG_END;
+               return SWITCH_STATUS_FALSE;
        }
 
-       memset(&osp_globals, 0, sizeof(osp_globals));
-       osp_globals.loglevel = SWITCH_LOG_DEBUG;
-       osp_globals.pool = pool;
-       osp_globals.protocol = OSPC_PROTNAME_SIP;
+       OSP_DEBUG_MSG("Parsing settings");
 
-       if ((settings = switch_xml_child(cfg, "settings"))) {
-               for (param = switch_xml_child(settings, "param"); param; param = param->next) {
-                       name = switch_xml_attr_soft(param, "name");
-                       value = switch_xml_attr_soft(param, "value");
-                       module = switch_xml_attr_soft(param, "module");
-                       context = switch_xml_attr_soft(param, "profile");
+       /* Init OSP module global status */
+       memset(&osp_global, 0, sizeof(osp_global));
+       osp_global.loglevel = SWITCH_LOG_DEBUG;
+       osp_global.protocol = OSPC_PROTNAME_SIP;
+       osp_global.pool = pool;
+
+       /* Get OSP module global settings */
+       if ((xsettings = switch_xml_child(xcfg, "settings"))) {
+               for (xparam = switch_xml_child(xsettings, "param"); xparam; xparam = xparam->next) {
+                       /* Settings parameter name */
+                       name = switch_xml_attr_soft(xparam, "name");
+                       /* Settings parameter value */
+                       value = switch_xml_attr_soft(xparam, "value");
+                       /* Endpoint module name */
+                       module = switch_xml_attr_soft(xparam, "module");
+                       /* Endpoint profile name */
+                       context = switch_xml_attr_soft(xparam, "profile");
+
+                       /* Ignore parameter without name */
                        if (switch_strlen_zero(name)) {
                                continue;
                        }
+
                        if (!strcasecmp(name, "debug-info")) {
+                               /* OSP module debug flag */
                                if (!switch_strlen_zero(value)) {
-                                       osp_globals.debug = switch_true(value);
+                                       osp_global.debug = switch_true(value);
                                }
+                               OSP_DEBUG("debug-info: '%d'", osp_global.debug);
                        } else if (!strcasecmp(name, "log-level")) {
+                               /* OSP module debug message log level */
                                if (switch_strlen_zero(value)) {
                                        continue;
                                } else if (!strcasecmp(value, "console")) {
-                                       osp_globals.loglevel = SWITCH_LOG_CONSOLE;
+                                       osp_global.loglevel = SWITCH_LOG_CONSOLE;
                                } else if (!strcasecmp(value, "alert")) {
-                                       osp_globals.loglevel = SWITCH_LOG_ALERT;
+                                       osp_global.loglevel = SWITCH_LOG_ALERT;
                                } else if (!strcasecmp(value, "crit")) {
-                                       osp_globals.loglevel = SWITCH_LOG_CRIT;
+                                       osp_global.loglevel = SWITCH_LOG_CRIT;
                                } else if (!strcasecmp(value, "error")) {
-                                       osp_globals.loglevel = SWITCH_LOG_ERROR;
+                                       osp_global.loglevel = SWITCH_LOG_ERROR;
                                } else if (!strcasecmp(value, "warning")) {
-                                       osp_globals.loglevel = SWITCH_LOG_WARNING;
+                                       osp_global.loglevel = SWITCH_LOG_WARNING;
                                } else if (!strcasecmp(value, "notice")) {
-                                       osp_globals.loglevel = SWITCH_LOG_NOTICE;
+                                       osp_global.loglevel = SWITCH_LOG_NOTICE;
                                } else if (!strcasecmp(value, "info")) {
-                                       osp_globals.loglevel = SWITCH_LOG_INFO;
+                                       osp_global.loglevel = SWITCH_LOG_INFO;
                                } else if (!strcasecmp(value, "debug")) {
-                                       osp_globals.loglevel = SWITCH_LOG_DEBUG;
+                                       osp_global.loglevel = SWITCH_LOG_DEBUG;
                                }
+                               OSP_DEBUG("log-level: '%d'", osp_global.loglevel);
                        } else if (!strcasecmp(name, "crypto-hardware")) {
+                               /* OSP module crypto hardware flag */
                                if (!switch_strlen_zero(value)) {
-                                       osp_globals.hardware = switch_true(value);
+                                       osp_global.hardware = switch_true(value);
                                }
+                               OSP_DEBUG("crypto-hardware: '%d'", osp_global.hardware);
                        } else if (!strcasecmp(name, "default-protocol")) {
+                               /* OSP module default signaling protocol */
                                if (switch_strlen_zero(value)) {
                                        continue;
                                } else if (!strcasecmp(value, OSP_PROTOCOL_SIP)) {
-                                       osp_globals.protocol = OSPC_PROTNAME_SIP;
+                                       osp_global.protocol = OSPC_PROTNAME_SIP;
                                } else if (!strcasecmp(value, OSP_PROTOCOL_H323)) {
-                                       osp_globals.protocol = OSPC_PROTNAME_Q931;
+                                       osp_global.protocol = OSPC_PROTNAME_Q931;
                                } else if (!strcasecmp(value, OSP_PROTOCOL_IAX)) {
-                                       osp_globals.protocol = OSPC_PROTNAME_IAX;
+                                       osp_global.protocol = OSPC_PROTNAME_IAX;
                                } else if (!strcasecmp(value, OSP_PROTOCOL_SKYPE)) {
-                                       osp_globals.protocol = OSPC_PROTNAME_SKYPE;
+                                       osp_global.protocol = OSPC_PROTNAME_SKYPE;
                                } else {
-                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported default protocol '%s'\n", value);
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported protocol '%s'\n", value);
                                }
-                       } else if (!strcasecmp(name, "sip")) {
+                               OSP_DEBUG("default-protocol: '%d'", osp_global.protocol);
+                       } else if (!strcasecmp(name, OSP_PROTOCOL_SIP)) {
+                               /* SIP endpoint module */
                                if (!switch_strlen_zero(module)) {
-                                       if (!(osp_globals.modules[OSPC_PROTNAME_SIP] = switch_core_strdup(osp_globals.pool, module))) {
+                                       if (!(osp_global.endpoint[OSPC_PROTNAME_SIP].module = switch_core_strdup(osp_global.pool, module))) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate SIP module name\n");
                                                status = SWITCH_STATUS_MEMERR;
                                                break;
                                        }
                                }
+                               /* SIP endpoint profile */
                                if (!switch_strlen_zero(context)) {
-                                       if (!(osp_globals.profiles[OSPC_PROTNAME_SIP] = switch_core_strdup(osp_globals.pool, context))) {
+                                       if (!(osp_global.endpoint[OSPC_PROTNAME_SIP].profile = switch_core_strdup(osp_global.pool, context))) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate SIP profile name\n");
                                                status = SWITCH_STATUS_MEMERR;
                                                break;
                                        }
                                }
-                       } else if (!strcasecmp(name, "h323")) {
+                               OSP_DEBUG("SIP: '%s/%s'", osp_global.endpoint[OSPC_PROTNAME_SIP].module, osp_global.endpoint[OSPC_PROTNAME_SIP].profile);
+                       } else if (!strcasecmp(name, OSP_PROTOCOL_H323)) {
+                               /* H.323 endpoint module */
                                if (!switch_strlen_zero(module)) {
-                                       if (!(osp_globals.modules[OSPC_PROTNAME_Q931] = switch_core_strdup(osp_globals.pool, module))) {
+                                       if (!(osp_global.endpoint[OSPC_PROTNAME_Q931].module = switch_core_strdup(osp_global.pool, module))) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate H.323 module name\n");
                                                status = SWITCH_STATUS_MEMERR;
                                                break;
                                        }
                                }
+                               /* H.323 endpoint profile */
                                if (!switch_strlen_zero(context)) {
-                                       if (!(osp_globals.profiles[OSPC_PROTNAME_Q931] = switch_core_strdup(osp_globals.pool, context))) {
+                                       if (!(osp_global.endpoint[OSPC_PROTNAME_Q931].profile = switch_core_strdup(osp_global.pool, context))) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate H.323 profile name\n");
                                                status = SWITCH_STATUS_MEMERR;
                                                break;
                                        }
                                }
-                       } else if (!strcasecmp(name, "iax")) {
+                               OSP_DEBUG("H.323: '%s/%s'", osp_global.endpoint[OSPC_PROTNAME_Q931].module, osp_global.endpoint[OSPC_PROTNAME_Q931].profile);
+                       } else if (!strcasecmp(name, OSP_PROTOCOL_IAX)) {
+                               /* IAX endpoint module */
                                if (!switch_strlen_zero(module)) {
-                                       if (!(osp_globals.modules[OSPC_PROTNAME_IAX] = switch_core_strdup(osp_globals.pool, module))) {
+                                       if (!(osp_global.endpoint[OSPC_PROTNAME_IAX].module = switch_core_strdup(osp_global.pool, module))) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate IAX module name\n");
                                                status = SWITCH_STATUS_MEMERR;
                                                break;
                                        }
                                }
+                               /* IAX endpoint profile */
                                if (!switch_strlen_zero(context)) {
-                                       if (!(osp_globals.profiles[OSPC_PROTNAME_IAX] = switch_core_strdup(osp_globals.pool, context))) {
+                                       if (!(osp_global.endpoint[OSPC_PROTNAME_IAX].profile = switch_core_strdup(osp_global.pool, context))) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate IAX profile name\n");
                                                status = SWITCH_STATUS_MEMERR;
                                                break;
                                        }
                                }
-                       } else if (!strcasecmp(name, "skype")) {
+                               OSP_DEBUG("IAX: '%s/%s'", osp_global.endpoint[OSPC_PROTNAME_IAX].module, osp_global.endpoint[OSPC_PROTNAME_IAX].profile);
+                       } else if (!strcasecmp(name, OSP_PROTOCOL_SKYPE)) {
+                               /* Skype endpoint module */
                                if (!switch_strlen_zero(module)) {
-                                       if (!(osp_globals.modules[OSPC_PROTNAME_SKYPE] = switch_core_strdup(osp_globals.pool, module))) {
+                                       if (!(osp_global.endpoint[OSPC_PROTNAME_SKYPE].module = switch_core_strdup(osp_global.pool, module))) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate Skype module name\n");
                                                status = SWITCH_STATUS_MEMERR;
                                                break;
                                        }
-                                       }
+                               }
+                               /* Skype endpoint profile */
                                if (!switch_strlen_zero(context)) {
-                                       if (!(osp_globals.profiles[OSPC_PROTNAME_SKYPE] = switch_core_strdup(osp_globals.pool, context))) {
+                                       if (!(osp_global.endpoint[OSPC_PROTNAME_SKYPE].profile = switch_core_strdup(osp_global.pool, context))) {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate Skype profile name\n");
                                                status = SWITCH_STATUS_MEMERR;
                                                break;
                                        }
                                }
+                               OSP_DEBUG("SKYPE: '%s/%s'", osp_global.endpoint[OSPC_PROTNAME_SKYPE].module, osp_global.endpoint[OSPC_PROTNAME_SKYPE].profile);
                        } else {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown parameter '%s'\n", name);
                        }
@@ -469,26 +494,37 @@ static switch_status_t osp_load_settings(
        }
 
        if (status != SWITCH_STATUS_SUCCESS) {
+               /* Fail for SWITCH_STATUS_MEMERR */
                switch_xml_free(xml);
+               OSP_DEBUG_END;
                return status;
        }
 
-       if ((profiles = switch_xml_child(cfg, "profiles"))) {
-               for (xprofile = switch_xml_child(profiles, "profile"); xprofile; xprofile = xprofile->next) {
+       /* Get OSP module profiles */
+       if ((xprofiles = switch_xml_child(xcfg, "profiles"))) {
+               for (xprofile = switch_xml_child(xprofiles, "profile"); xprofile; xprofile = xprofile->next) {
+                       /* Profile name */
                        name = switch_xml_attr_soft(xprofile, "name");
                        if (switch_strlen_zero(name)) {
                                name = OSP_DEF_PROFILE;
                        }
+                       OSP_DEBUG("Parsing profile '%s'", name);
+
+                       /* Check duplate profile name */
                        if (osp_find_profile(name, NULL) == SWITCH_STATUS_SUCCESS) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignored duplicate profile '%s'\n", name);
                                continue;
                        }
-                       if (!(profile = switch_core_alloc(osp_globals.pool, sizeof(*profile)))) {
+
+                       /* Allocate profile */
+                       if (!(profile = switch_core_alloc(osp_global.pool, sizeof(*profile)))) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to alloc profile\n");
                                status = SWITCH_STATUS_MEMERR;
                                break;
                        }
-                       if (!(profile->name = switch_core_strdup(osp_globals.pool, name))) {
+
+                       /* Store profile name */
+                       if (!(profile->name = switch_core_strdup(osp_global.pool, name))) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate profile name\n");
                                status = SWITCH_STATUS_MEMERR;
                                /* "profile" cannot free to pool in FreeSWITCH */
@@ -505,62 +541,80 @@ static switch_status_t osp_load_settings(
                        profile->maxdest = OSP_DEF_MAXDEST;
                        profile->provider = OSP_INVALID_HANDLE;
 
-                       for (param = switch_xml_child(xprofile, "param"); param; param = param->next) {
-                               name = switch_xml_attr_soft(param, "name");
-                               value = switch_xml_attr_soft(param, "value");
+                       for (xparam = switch_xml_child(xprofile, "param"); xparam; xparam = xparam->next) {
+                               /* Profile parameter name */
+                               name = switch_xml_attr_soft(xparam, "name");
+                               /* Profile parameter value */
+                               value = switch_xml_attr_soft(xparam, "value");
+
+                               /* Ignore profile parameter without name or value */
                                if (switch_strlen_zero(name) || switch_strlen_zero(value)) {
                                        continue;
                                }
+
                                if (!strcasecmp(name, "service-point-url")) {
-                                       if (profile->spnum < OSP_MAX_SP) {
-                                               profile->spurls[profile->spnum] = switch_core_strdup(osp_globals.pool, value);
-                                               profile->spnum++;
+                                       /* OSP service point URL */
+                                       if (profile->spnumber < OSP_MAX_SPNUMBER) {
+                                               profile->spurl[profile->spnumber] = switch_core_strdup(osp_global.pool, value);
+                                               OSP_DEBUG("service-point-url[%d]: '%s'", profile->spnumber, profile->spurl[profile->spnumber]);
+                                               profile->spnumber++;
                                        } else {
-                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignored excess service point '%s'\n", value);
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignored service point '%s'\n", value);
                                        }
                                } else if (!strcasecmp(name, "device-ip")) {
-                                       profile->device = switch_core_strdup(osp_globals.pool, value);
+                                       /* OSP client end IP */
+                                       profile->deviceip = switch_core_strdup(osp_global.pool, value);
+                                       OSP_DEBUG("device-ip: '%s'", profile->deviceip);
                                } else if (!strcasecmp(name, "ssl-lifetime")) {
+                                       /* SSL lifetime */
                                        if (sscanf(value, "%d", &number) == 1) {
                                                profile->lifetime = number;
                                        } else {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "ssl-lifetime must be a number\n");
                                        }
+                                       OSP_DEBUG("ssl-lifetime: '%d'", profile->lifetime);
                                } else if (!strcasecmp(name, "http-max-connections")) {
+                                       /* HTTP max connections */
                                        if ((sscanf(value, "%d", &number) == 1) && (number >= OSP_MIN_MAXCONN) && (number <= OSP_MAX_MAXCONN)) {
                                                profile->maxconnect = number;
                                        } else {
-                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
-                                                       "http-max-connections must be between %d and %d\n", OSP_MIN_MAXCONN, OSP_MAX_MAXCONN);
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "http-max-connections must be between %d and %d\n", OSP_MIN_MAXCONN, OSP_MAX_MAXCONN);
                                        }
+                                       OSP_DEBUG("http-max-connections: '%d'", profile->maxconnect);
                                } else if (!strcasecmp(name, "http-persistence")) {
+                                       /* HTTP persistence */
                                        if (sscanf(value, "%d", &number) == 1) {
                                                profile->persistence = number;
                                        } else {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "http-persistence must be a number\n");
                                        }
+                                       OSP_DEBUG("http-persistence: '%d'", profile->persistence);
                                } else if (!strcasecmp(name, "http-retry-delay")) {
+                                       /* HTTP retry delay */
                                        if ((sscanf(value, "%d", &number) == 1) && (number >= OSP_MIN_RETRYDELAY) && (number <= OSP_MAX_RETRYDELAY)) {
                                                profile->retrydelay = number;
                                        } else {
-                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
-                                                       "http-retry-delay must be between %d and %d\n", OSP_MIN_RETRYDELAY, OSP_MAX_RETRYDELAY);
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "http-retry-delay must be between %d and %d\n", OSP_MIN_RETRYDELAY, OSP_MAX_RETRYDELAY);
                                        }
+                                       OSP_DEBUG("http-retry-delay: '%d'", profile->retrydelay);
                                } else if (!strcasecmp(name, "http-retry-limit")) {
+                                       /* HTTP retry limit */
                                        if ((sscanf(value, "%d", &number) == 1) && (number >= OSP_MIN_RETRYLIMIT) && (number <= OSP_MAX_RETRYLIMIT)) {
                                                profile->retrylimit = number;
                                        } else {
-                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
-                                                       "http-retry-limit must be between %d and %d\n", OSP_MIN_RETRYLIMIT, OSP_MAX_RETRYLIMIT);
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "http-retry-limit must be between %d and %d\n", OSP_MIN_RETRYLIMIT, OSP_MAX_RETRYLIMIT);
                                        }
+                                       OSP_DEBUG("http-retry-limit: '%d'", profile->retrylimit);
                                } else if (!strcasecmp(name, "http-timeout")) {
+                                       /* HTTP timeout value */
                                        if ((sscanf(value, "%d", &number) == 1) && (number >= OSP_MIN_TIMEOUT) && (number <= OSP_MAX_TIMEOUT)) {
                                                profile->timeout = number;
                                        } else {
-                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
-                                                       "http-timeout must be between %d and %d\n", OSP_MIN_TIMEOUT, OSP_MAX_TIMEOUT);
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "http-timeout must be between %d and %d\n", OSP_MIN_TIMEOUT, OSP_MAX_TIMEOUT);
                                        }
+                                       OSP_DEBUG("http-timeout: '%d'", profile->timeout);
                                } else if (!strcasecmp(name, "work-mode")) {
+                                       /* OSP work mode */
                                        if (!strcasecmp(value, "direct")) {
                                                profile->workmode = OSP_MODE_DIRECT;
                                        } else if (!strcasecmp(value, "indirect")) {
@@ -568,7 +622,9 @@ static switch_status_t osp_load_settings(
                                        } else {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown work mode '%s'\n", value);
                                        }
+                                       OSP_DEBUG("work-mode: '%d'", profile->workmode);
                                } else if (!strcasecmp(name, "service-type")) {
+                                       /* OSP service type */
                                        if (!strcasecmp(value, "voice")) {
                                                profile->srvtype = OSP_SRV_VOICE;
                                        } else if (!strcasecmp(value, "npquery")) {
@@ -576,19 +632,22 @@ static switch_status_t osp_load_settings(
                                        } else {
                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown service type '%s'\n", value);
                                        }
+                                       OSP_DEBUG("service-type: '%d'", profile->srvtype);
                                } else if (!strcasecmp(name, "max-destinations")) {
+                                       /* Max destinations */
                                        if ((sscanf(value, "%d", &number) == 1) && (number >= OSP_MIN_MAXDEST) && (number <= OSP_MAX_MAXDEST)) {
                                                profile->maxdest = number;
                                        } else {
-                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
-                                                       "max-destinations must be between %d and %d\n", OSP_MIN_MAXDEST, OSP_MAX_MAXDEST);
+                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "max-destinations must be between %d and %d\n", OSP_MIN_MAXDEST, OSP_MAX_MAXDEST);
                                        }
+                                       OSP_DEBUG("max-destinations: '%d'", profile->maxdest);
                                } else {
                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown parameter '%s'\n", name);
                                }
                        }
 
-                       if (!profile->spnum) {
+                       /* Check number of service porints */
+                       if (!profile->spnumber) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Without service point URI in profile '%s'\n", profile->name);
                                /* "profile" cannot free to pool in FreeSWITCH */
                                continue;
@@ -601,14 +660,11 @@ static switch_status_t osp_load_settings(
 
        switch_xml_free(xml);
 
+       OSP_DEBUG_END;
+
        return status;
 }
 
-/* OSP default certificates */
-const char *B64PKey = "MIIBOgIBAAJBAK8t5l+PUbTC4lvwlNxV5lpl+2dwSZGW46dowTe6y133XyVEwNiiRma2YNk3xKs/TJ3Wl9Wpns2SYEAJsFfSTukCAwEAAQJAPz13vCm2GmZ8Zyp74usTxLCqSJZNyMRLHQWBM0g44Iuy4wE3vpi7Wq+xYuSOH2mu4OddnxswCP4QhaXVQavTAQIhAOBVCKXtppEw9UaOBL4vW0Ed/6EA/1D8hDW6St0h7EXJAiEAx+iRmZKhJD6VT84dtX5ZYNVk3j3dAcIOovpzUj9a0CECIEduTCapmZQ5xqAEsLXuVlxRtQgLTUD4ZxDElPn8x0MhAiBE2HlcND0+qDbvtwJQQOUzDgqg5xk3w8capboVdzAlQQIhAMC+lDL7+gDYkNAft5Mu+NObJmQs4Cr+DkDFsKqoxqrm";
-const char *B64LCert = "MIIBeTCCASMCEHqkOHVRRWr+1COq3CR/xsowDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTA1MDYyMzAwMjkxOFoXDTA2MDYyNDAwMjkxOFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCvLeZfj1G0wuJb8JTcVeZaZftncEmRluOnaME3ustd918lRMDYokZmtmDZN8SrP0yd1pfVqZ7NkmBACbBX0k7pAgMBAAEwDQYJKoZIhvcNAQEEBQADQQDnV8QNFVVJx/+7IselU0wsepqMurivXZzuxOmTEmTVDzCJx1xhA8jd3vGAj7XDIYiPub1PV23eY5a2ARJuw5w9";
-const char *B64CACert = "MIIBYDCCAQoCAQEwDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTAyMDIwNDE4MjU1MloXDTEyMDIwMzE4MjU1MlowOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPGeGwV41EIhX0jEDFLRXQhDEr50OUQPq+f55VwQd0TQNts06BP29+UiNdRW3c3IRHdZcJdC1Cg68ME9cgeq0h8CAwEAATANBgkqhkiG9w0BAQQFAANBAGkzBSj1EnnmUxbaiG1N4xjIuLAWydun7o3bFk2tV8dBIhnuh445obYyk1EnQ27kI7eACCILBZqi2MHDOIMnoN0=";
-
 /*
  * Init OSP client end
  * return
@@ -625,16 +681,21 @@ static void osp_init_osptk(void)
        unsigned char cacertdata[OSP_SIZE_KEYSTR];
        int error;
 
-       if (osp_globals.hardware) {
+       OSP_DEBUG_START;
+
+       /* Init OSP Toolkit */
+       if (osp_global.hardware) {
                if ((error = OSPPInit(OSPC_TRUE)) != OSPC_ERR_NO_ERROR) {
+                       /* Unable to enable crypto hardware, disable it */
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to enable crypto hardware, error '%d'\n", error);
-                       osp_globals.hardware = SWITCH_FALSE;
+                       osp_global.hardware = SWITCH_FALSE;
                        OSPPInit(OSPC_FALSE);
                }
        } else {
                OSPPInit(OSPC_FALSE);
        }
 
+       /* Init OSP profile, using default certificates  */
        for (profile = osp_profiles; profile; profile = profile->next) {
                privatekey.PrivateKeyData = privatekeydata;
                privatekey.PrivateKeyLength = sizeof(privatekeydata);
@@ -655,9 +716,10 @@ static void osp_init_osptk(void)
                }
 
                if (error == OSPC_ERR_NO_ERROR) {
+                       /* Create provider handle */
                        error = OSPPProviderNew(
-                               profile->spnum,                 /* Number of service points */
-                               profile->spurls,                /* Service point URLs */
+                               profile->spnumber,              /* Number of service points */
+                               profile->spurl                /* Service point URLs */
                                NULL,                                   /* Weights */
                                OSP_AUDIT_URL,                  /* Audit URL */
                                &privatekey,                    /* Provate key */
@@ -677,21 +739,52 @@ static void osp_init_osptk(void)
                        if (error != OSPC_ERR_NO_ERROR) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to create provider for profile %s, error '%d'\n", profile->name, error);
                                profile->provider = OSP_INVALID_HANDLE;
+                       } else {
+                               OSP_DEBUG("Created provider handle for profile '%s'", profile->name);
                        }
                }
        }
+
+       OSP_DEBUG_END;
+}
+
+/*
+ * Cleanup OSP client end
+ * return
+ */
+static void osp_cleanup_osptk(void)
+{
+       osp_profile_t *profile;
+
+       OSP_DEBUG_START;
+
+       for (profile = osp_profiles; profile; profile = profile->next) {
+               if (profile->provider != OSP_INVALID_HANDLE) {
+                       /* Delete provider handle */
+                       OSPPProviderDelete(profile->provider, 0);
+                       profile->provider = OSP_INVALID_HANDLE;
+                       OSP_DEBUG("Deleted provider handle for profile '%s'", profile->name);
+               }
+       }
+
+       /* Cleanup OSP Toolkit */
+       OSPPCleanup();
+
+       OSP_DEBUG_END;
 }
 
 /*
  * Get protocol name
- * param protocol Supported protocol
+ * param protocol Protocol
  * return protocol name
  */
-static const char *osp_get_protocol(
+static const char *osp_get_protocolname(
        OSPE_PROTOCOL_NAME protocol)
 {
        const char *name;
 
+       OSP_DEBUG_START;
+
        switch (protocol) {
        case OSPC_PROTNAME_UNKNOWN:
                name = OSP_PROTOCOL_UNKNO;
@@ -721,145 +814,45 @@ static const char *osp_get_protocol(
                name = OSP_PROTOCOL_UNSUP;
                break;
        }
+       OSP_DEBUG("Protocol %d: '%s'", protocol, name);
+
+       OSP_DEBUG_END;
 
        return name;
 }
 
 /*
- * Macro expands to:
- * static switch_status_t osp_api_function(_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream)
+ * Get protocol from module name
+ * param module Endpoint module name
+ * return protocol Endpoint protocol name
  */
-SWITCH_STANDARD_API(osp_api_function)
+static OSPE_PROTOCOL_NAME osp_get_protocol(
+       const char *module)
 {
-       int i, argc = 0;
-       char *argv[2] = { 0 };
-       char *params = NULL;
-       char *param = NULL;
-       osp_profile_t *profile;
-       char *loglevel;
-
-       if (session) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function cannot be called from the dialplan.\n");
-               return SWITCH_STATUS_FALSE;
-       }
-
-       if (switch_strlen_zero(cmd)) {
-               stream->write_function(stream, "Usage: osp status\n");
-               return SWITCH_STATUS_SUCCESS;
-       }
+       OSPE_PROTOCOL_NAME protocol;
 
-       if (!(params = switch_safe_strdup(cmd))) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate parameters\n");
-               return SWITCH_STATUS_MEMERR;
-       }
+       OSP_DEBUG_START;
 
-       if ((argc = switch_separate_string(params, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
-               param = argv[0];
-               if (!strcasecmp(param, "status")) {
-                       stream->write_function(stream, "=============== OSP Module Settings & Status ===============\n");
-                       stream->write_function(stream, "                debug-info: %s\n", osp_globals.debug ? "enabled" : "disabled");
-                       switch (osp_globals.loglevel) {
-                       case SWITCH_LOG_CONSOLE:
-                               loglevel = "console";
-                               break;
-                       case SWITCH_LOG_ALERT:
-                               loglevel = "alert";
-                               break;
-                       case SWITCH_LOG_CRIT:
-                               loglevel = "crit";
-                               break;
-                       case SWITCH_LOG_ERROR:
-                               loglevel = "error";
-                               break;
-                       case SWITCH_LOG_WARNING:
-                               loglevel = "warning";
-                               break;
-                       case SWITCH_LOG_NOTICE:
-                               loglevel = "notice";
-                               break;
-                       case SWITCH_LOG_INFO:
-                               loglevel = "info";
-                               break;
-                       case SWITCH_LOG_DEBUG:
-                       default:
-                               loglevel = "debug";
-                               break;
-                       }
-                       stream->write_function(stream, "                 log-level: %s\n", loglevel);
-                       stream->write_function(stream, "           crypto-hardware: %s\n", osp_globals.hardware ? "enabled" : "disabled");
-                       if (switch_strlen_zero(osp_globals.modules[OSPC_PROTNAME_SIP]) || switch_strlen_zero(osp_globals.profiles[OSPC_PROTNAME_SIP])) {
-                               stream->write_function(stream, "                       sip: unsupported\n");
-                       } else {
-                               stream->write_function(stream, "                       sip: %s/%s\n",
-                                       osp_globals.modules[OSPC_PROTNAME_SIP], osp_globals.profiles[OSPC_PROTNAME_SIP]);
-                       }
-                       if (switch_strlen_zero(osp_globals.modules[OSPC_PROTNAME_Q931]) || switch_strlen_zero(osp_globals.profiles[OSPC_PROTNAME_Q931])) {
-                               stream->write_function(stream, "                      h323: unsupported\n");
-                       } else {
-                               stream->write_function(stream, "                      h323: %s/%s\n",
-                                       osp_globals.modules[OSPC_PROTNAME_Q931], osp_globals.profiles[OSPC_PROTNAME_Q931]);
-                       }
-                       if (switch_strlen_zero(osp_globals.modules[OSPC_PROTNAME_IAX]) || switch_strlen_zero(osp_globals.profiles[OSPC_PROTNAME_IAX])) {
-                               stream->write_function(stream, "                       iax: unsupported\n");
-                       } else {
-                               stream->write_function(stream, "                       iax: %s/%s\n",
-                                       osp_globals.modules[OSPC_PROTNAME_IAX], osp_globals.profiles[OSPC_PROTNAME_IAX]);
-                       }
-                       if (switch_strlen_zero(osp_globals.modules[OSPC_PROTNAME_SKYPE]) || switch_strlen_zero(osp_globals.profiles[OSPC_PROTNAME_SKYPE])) {
-                               stream->write_function(stream, "                     skype: unsupported\n");
-                       } else {
-                               stream->write_function(stream, "                     skype: %s/%s\n",
-                                       osp_globals.modules[OSPC_PROTNAME_SKYPE], osp_globals.profiles[OSPC_PROTNAME_SKYPE]);
-                       }
-                       stream->write_function(stream, "          default-protocol: %s\n", osp_get_protocol(osp_globals.protocol));
-                       stream->write_function(stream, "============== OSP Profile Settings & Status ==============\n");
-                       for (profile = osp_profiles; profile; profile = profile->next) {
-                               stream->write_function(stream, "Profile: %s\n", profile->name);
-                               for (i = 0; i < profile->spnum; i++) {
-                                       stream->write_function(stream, "         service-point-url: %s\n", profile->spurls[i]);
-                               }
-                               stream->write_function(stream, "                 device-ip: %s\n", profile->device);
-                               stream->write_function(stream, "              ssl-lifetime: %d\n", profile->lifetime);
-                               stream->write_function(stream, "      http-max-connections: %d\n", profile->maxconnect);
-                               stream->write_function(stream, "          http-persistence: %d\n", profile->persistence);
-                               stream->write_function(stream, "          http-retry-dalay: %d\n", profile->retrydelay);
-                               stream->write_function(stream, "          http-retry-limit: %d\n", profile->retrylimit);
-                               stream->write_function(stream, "              http-timeout: %d\n", profile->timeout);
-                               switch (profile->workmode) {
-                               case OSP_MODE_DIRECT:
-                                       stream->write_function(stream, "                 work-mode: direct\n");
-                                       break;
-                               case OSP_MODE_INDIRECT:
-                               default:
-                                       stream->write_function(stream, "                 work-mode: indirect\n");
-                                       break;
-                               }
-                               switch (profile->srvtype) {
-                               case OSP_SRV_NPQUERY:
-                                       stream->write_function(stream, "              service-type: npquery\n");
-                                       break;
-                               case OSP_SRV_VOICE:
-                               default:
-                                       stream->write_function(stream, "              service-type: voice\n");
-                                       break;
-                               }
-                               stream->write_function(stream, "          max-destinations: %d\n", profile->maxdest);
-                               stream->write_function(stream, "                    status: %s\n", profile->provider != OSP_INVALID_HANDLE ? "enabled" : "disabled");
-                       }
-               } else {
-                       stream->write_function(stream, "Invalid Syntax!\n");
-               }
+       if (!strcasecmp(module, OSP_MODULE_SIP)) {
+               protocol = OSPC_PROTNAME_SIP;
+       } else if (!strcasecmp(module, OSP_MODULE_H323)) {
+               protocol = OSPC_PROTNAME_Q931;
+       } else if (!strcasecmp(module, OSP_MODULE_IAX)) {
+               protocol = OSPC_PROTNAME_IAX;
+       } else if (!strcasecmp(module, OSP_MODULE_SKYPE)) {
+               protocol = OSPC_PROTNAME_SKYPE;
        } else {
-               stream->write_function(stream, "Invalid Input!\n");
+               protocol = OSPC_PROTNAME_UNKNOWN;
        }
+       OSP_DEBUG("Module %s: '%d'", module, protocol);
 
-       switch_safe_free(params);
+       OSP_DEBUG_END;
 
-       return SWITCH_STATUS_SUCCESS;
+       return protocol;
 }
 
 /*
- * Parse Userinfo for user and LNP
+ * Parse userinfo for user and LNP
  * param userinfo SIP URI userinfo
  * param user User part
  * param usersize Size of user buffer
@@ -884,26 +877,60 @@ static void osp_parse_userinfo(
        char *item;
        char *tmp;
 
+       OSP_DEBUG_START;
+
+       /* Set default values */
+       if (user && usersize) {
+               user[0] = '\0';
+       }
+       if (rn && rnsize) {
+               rn[0] = '\0';
+       }
+       if (cic && cicsize) {
+               cic[0] = '\0';
+       }
+       if (npdi) {
+               *npdi = 0;
+       }
+
+       /* Parse userinfo */
        if (!switch_strlen_zero(userinfo)) {
+               /* Copy userinfo to temporary buffer */
                switch_copy_string(buffer, userinfo, sizeof(buffer));
+
+               /* Parse user */
                item = strtok_r(buffer, OSP_USER_DELIM, &tmp);
                if (user && usersize) {
                        switch_copy_string(user, item, usersize);
                }
+
+               /* Parse LNP parameters */
                for (item = strtok_r(NULL, OSP_USER_DELIM, &tmp); item; item = strtok_r(NULL, OSP_USER_DELIM, &tmp)) {
                        if (!strncasecmp(item, "rn=", 3)) {
+                               /* Parsed routing number */
                                if (rn && rnsize) {
                                        switch_copy_string(rn, item + 3, rnsize);
                                }
                        } else if (!strncasecmp(item, "cic=", 4)) {
+                               /* Parsed cic */
                                if (cic && cicsize) {
                                        switch_copy_string(cic, item + 4, cicsize);
                                }
                        } else if (!strcasecmp(item, "npdi")) {
-                               *npdi = 1;
+                               /* Parsed npdi */
+                               if (npdi) {
+                                       *npdi = 1;
+                               }
                        }
                }
        }
+
+       OSP_DEBUG("user: '%s'", OSP_FILTER_NULLSTR(user));
+       OSP_DEBUG("rn: '%s'", OSP_FILTER_NULLSTR(rn));
+       OSP_DEBUG("cic: '%s'", OSP_FILTER_NULLSTR(cic));
+       OSP_DEBUG("npdi: '%d'", OSP_FILTER_NULLINT(npdi));
+
+       OSP_DEBUG_END;
 }
 
 /*
@@ -923,18 +950,30 @@ static void osp_parse_header_user(
        char *tmp;
        char *item;
 
-       if (!switch_strlen_zero(header) && user && usersize) {
-               *user = '\0';
-               switch_copy_string(buffer, header, sizeof(buffer));
-               if ((head = strstr(buffer, "sip:"))) {
-                       head += 4;
-                       if ((tmp = strchr(head, OSP_URI_DELIM))) {
-                               *tmp = '\0';
-                               item = strtok_r(head, OSP_USER_DELIM, &tmp);
-                               switch_copy_string(user, item, usersize);
+       OSP_DEBUG_START;
+
+       if (user && usersize) {
+               user[0] = '\0';
+
+               /* Parse user */
+               if (!switch_strlen_zero(header) && user && usersize) {
+                       /* Copy header to temporary buffer */
+                       switch_copy_string(buffer, header, sizeof(buffer));
+
+                       if ((head = strstr(buffer, "sip:"))) {
+                               head += 4;
+                               if ((tmp = strchr(head, OSP_URI_DELIM))) {
+                                       *tmp = '\0';
+                                       item = strtok_r(head, OSP_USER_DELIM, &tmp);
+                                       switch_copy_string(user, item, usersize);
+                               }
                        }
                }
+
+               OSP_DEBUG("user: '%s'", user);
        }
+
+       OSP_DEBUG_END;
 }
 
 /*
@@ -953,128 +992,292 @@ static void osp_parse_header_host(
        char *head;
        char *tmp;
 
-       if (!switch_strlen_zero(header) && host && hostsize) {
-               *host = '\0';
-               switch_copy_string(buffer, header, sizeof(buffer));
-               if ((head = strstr(buffer, "sip:"))) {
-                       head += 4;
-                       if ((tmp = strchr(head, OSP_URI_DELIM))) {
-                               head = tmp + 1;
+       OSP_DEBUG_START;
+
+       if (hostsize) {
+               host[0] = '\0';
+
+               /* Parse host*/
+               if (!switch_strlen_zero(header) && host && hostsize) {
+                       /* Copy header to temporary buffer */
+                       switch_copy_string(buffer, header, sizeof(buffer));
+
+                       if ((head = strstr(buffer, "sip:"))) {
+                               head += 4;
+                               if ((tmp = strchr(head, OSP_URI_DELIM))) {
+                                       head = tmp + 1;
+                               }
+                               tmp = strtok(head, OSP_HOST_DELIM);
+                               switch_copy_string(host, tmp, hostsize);
                        }
-                       tmp = strtok(head, OSP_HOST_DELIM);
-                       switch_copy_string(host, tmp, hostsize);
                }
+
+               OSP_DEBUG("host: '%s'", host);
        }
+
+       OSP_DEBUG_END;
 }
 
 /*
- * Log AuthReq parameters
- * param profile OSP profile
- * param inbound Inbound info
+ * Convert "address:port" to "[x.x.x.x]:port" or "hostname:port" format
+ * param src Source address string
+ * param dest Destination address string
+ * param destsize Size of dest buffer
  * return
  */
-static void osp_log_authreq(
-       osp_profile_t *profile,
-       osp_inbound_t *inbound)
+static void osp_convert_inout(
+       const char *src,
+       char *dest,
+       int destsize)
 {
-       char *srvtype;
-       const char *source;
-       const char *srcdev;
-       char term[OSP_SIZE_NORSTR];
-       int total;
+       struct in_addr inp;
+       char buffer[OSP_SIZE_NORSTR];
+       char *port;
 
-       if (osp_globals.debug) {
-               if (profile->workmode == OSP_MODE_INDIRECT) {
-                       source = inbound->srcdev;
-                       if (switch_strlen_zero(inbound->actsrc)) {
-                               srcdev = inbound->srcdev;
-                       } else {
-                               srcdev = inbound->actsrc;
+       OSP_DEBUG_START;
+
+       if (dest && destsize) {
+               dest[0] = '\0';
+
+               if (!switch_strlen_zero(src)) {
+                       /* Copy IP address to temporary buffer */
+                       switch_copy_string(buffer, src, sizeof(buffer));
+
+                       if((port = strchr(buffer, ':'))) {
+                               *port = '\0';
+                               port++;
                        }
-               } else {
-                       source = profile->device;
-                       srcdev = inbound->srcdev;
-               }
 
-               if (profile->srvtype == OSP_SRV_NPQUERY) {
-                       srvtype = "npquery";
-                       if (switch_strlen_zero(inbound->tohost)) {
-                               switch_copy_string(term, source, sizeof(term));
-                       } else {
-                               if (switch_strlen_zero(inbound->toport)) {
-                                       switch_copy_string(term, inbound->tohost, sizeof(term));
+                       if (inet_pton(AF_INET, buffer, &inp) == 1) {
+                               if (port) {
+                                       switch_snprintf(dest, destsize, "[%s]:%s", buffer, port);
                                } else {
-                                       switch_snprintf(term, sizeof(term), "%s:%s", inbound->tohost, inbound->toport);
+                                       switch_snprintf(dest, destsize, "[%s]", buffer);
                                }
+                               dest[destsize - 1] = '\0';
+                       } else {
+                               switch_copy_string(dest, src, destsize);
                        }
-                       total = 1;
-               } else {
-                       srvtype = "voice";
-                       term[0] = '\0';
-                       total = profile->maxdest;
                }
 
-               switch_log_printf(SWITCH_CHANNEL_LOG, osp_globals.loglevel,
-                       "AuthReq: "
-                       "srvtype '%s' "
-                       "source '%s' "
-                       "srcdev '%s' "
-                       "protocol '%s' "
-                       "calling '%s' "
-                       "called '%s' "
-                       "lnp '%s/%s/%d' "
-                       "prefer '%s' "
-                       "rpid '%s' "
-                       "pai '%s' "
-                       "div '%s/%s' "
-                       "pci '%s' "
-                       "srcnid '%s' "
-                       "cinfo '%s/%s/%s/%s/%s/%s/%s/%s' "
-                       "maxcount '%d'\n",
-                       srvtype,
-                       source,
-                       srcdev,
-                       osp_get_protocol(inbound->protocol),
-                       inbound->calling,
-                       inbound->called,
-                       inbound->nprn, inbound->npcic, inbound->npdi,
-                       term,
-                       inbound->rpiduser,
-                       inbound->paiuser,
-                       inbound->divuser, inbound->divhost,
-                       inbound->pciuser,
-                       osp_filter_null(inbound->srcnid),
-                       osp_filter_null(inbound->cinfo[0]), osp_filter_null(inbound->cinfo[1]),
-                       osp_filter_null(inbound->cinfo[2]), osp_filter_null(inbound->cinfo[3]),
-                       osp_filter_null(inbound->cinfo[4]), osp_filter_null(inbound->cinfo[5]),
-                       osp_filter_null(inbound->cinfo[6]), osp_filter_null(inbound->cinfo[7]),
-                       total);
+               OSP_DEBUG("out: '%s'", dest);
        }
+
+       OSP_DEBUG_END;
 }
 
 /*
- * Get protocol from module name
- * param module Module name
- * return protocol name
- */
-static OSPE_PROTOCOL_NAME osp_get_moduleprotocol(
-       const char *module)
-{
-       OSPE_PROTOCOL_NAME protocol;
+ * Convert "[x.x.x.x]:port" or "hostname:prot" to "address:port" format
+ * param src Source address string
+ * param dest Destination address string
+ * param destsize Size of dest buffer
+ * return
+ */
+static void osp_convert_outin(
+       const char *src,
+       char *dest,
+       int destsize)
+{
+       char buffer[OSP_SIZE_NORSTR];
+       char *end;
+       char *port;
 
-       if (!strcasecmp(module, OSP_MODULE_SIP)) {
-               protocol = OSPC_PROTNAME_SIP;
-       } else if (!strcasecmp(module, OSP_MODULE_H323)) {
-               protocol = OSPC_PROTNAME_Q931;
-       } else if (!strcasecmp(module, OSP_MODULE_IAX)) {
-               protocol = OSPC_PROTNAME_IAX;
-       } else if (!strcasecmp(module, OSP_MODULE_SKYPE)) {
-               protocol = OSPC_PROTNAME_SKYPE;
+       OSP_DEBUG_START;
+
+       if (dest && destsize) {
+               dest[0] = '\0';
+
+               if (!switch_strlen_zero(src)) {
+                       switch_copy_string(buffer, src, sizeof(buffer));
+
+                       if (buffer[0] == '[') {
+                               if((port = strchr(buffer + 1, ':'))) {
+                                       *port = '\0';
+                                       port++;
+                               }
+
+                               if ((end = strchr(buffer + 1, ']'))) {
+                                       *end = '\0';
+                               }
+
+                               if (port) {
+                                       switch_snprintf(dest, destsize, "%s:%s", buffer + 1, port);
+                                       dest[destsize - 1] = '\0';
+                               } else {
+                                       switch_copy_string(dest, buffer + 1, destsize);
+                               }
+                       } else {
+                               switch_copy_string(dest, src, destsize);
+                       }
+               }
+               OSP_DEBUG("in: '%s'", dest);
+       }
+
+       OSP_DEBUG_END;
+}
+
+/*
+ * Always log AuthReq parameters
+ * param profile OSP profile
+ * param inbound Inbound info
+ * return
+ */
+static void osp_log_authreq(
+       osp_profile_t *profile,
+       osp_inbound_t *inbound)
+{
+       char *srvtype;
+       const char *source;
+       const char *srcdev;
+       char term[OSP_SIZE_NORSTR];
+       int total;
+
+       OSP_DEBUG_START;
+
+       /* Get source device and source */
+       if (profile->workmode == OSP_MODE_INDIRECT) {
+               source = inbound->srcdev;
+               if (switch_strlen_zero(inbound->actsrc)) {
+                       srcdev = inbound->srcdev;
+               } else {
+                       srcdev = inbound->actsrc;
+               }
        } else {
-               protocol = OSPC_PROTNAME_UNKNOWN;
+               source = profile->deviceip;
+               srcdev = inbound->srcdev;
        }
 
-       return protocol;
+       /* Get preferred destination for NP query */
+       if (profile->srvtype == OSP_SRV_NPQUERY) {
+               srvtype = "npquery";
+               if (switch_strlen_zero(inbound->tohost)) {
+                       switch_copy_string(term, source, sizeof(term));
+               } else {
+                       if (switch_strlen_zero(inbound->toport)) {
+                               switch_copy_string(term, inbound->tohost, sizeof(term));
+                       } else {
+                               switch_snprintf(term, sizeof(term), "%s:%s", inbound->tohost, inbound->toport);
+                       }
+               }
+               total = 1;
+       } else {
+               srvtype = "voice";
+               term[0] = '\0';
+               total = profile->maxdest;
+       }
+
+       switch_log_printf(SWITCH_CHANNEL_LOG, osp_global.loglevel,
+               "AuthReq: "
+               "srvtype '%s' "
+               "source '%s' "
+               "srcdev '%s' "
+               "srcnid '%s' "
+               "protocol '%s' "
+               "callid '%s' "
+               "calling '%s' "
+               "called '%s' "
+               "lnp '%s/%s/%d' "
+               "preferred '%s' "
+               "rpid '%s' "
+               "pai '%s' "
+               "div '%s/%s' "
+               "pci '%s' "
+               "cinfo '%s/%s/%s/%s/%s/%s/%s/%s' "
+               "maxcount '%d'\n",
+               srvtype,
+               source,
+               srcdev,
+               OSP_FILTER_NULLSTR(inbound->srcnid),
+               osp_get_protocolname(inbound->protocol),
+               OSP_FILTER_NULLSTR(inbound->callid),
+               inbound->calling,
+               inbound->called,
+               inbound->nprn, inbound->npcic, inbound->npdi,
+               term,
+               inbound->rpiduser,
+               inbound->paiuser,
+               inbound->divuser, inbound->divhost,
+               inbound->pciuser,
+               OSP_FILTER_NULLSTR(inbound->cinfo[0]), OSP_FILTER_NULLSTR(inbound->cinfo[1]), OSP_FILTER_NULLSTR(inbound->cinfo[2]), OSP_FILTER_NULLSTR(inbound->cinfo[3]),
+               OSP_FILTER_NULLSTR(inbound->cinfo[4]), OSP_FILTER_NULLSTR(inbound->cinfo[5]), OSP_FILTER_NULLSTR(inbound->cinfo[6]), OSP_FILTER_NULLSTR(inbound->cinfo[7]),
+               total);
+
+       OSP_DEBUG_END;
+}
+
+/*
+ * Always log AuthRsp parameters
+ * param results Routing info
+ * return
+ */
+static void osp_log_authrsp(
+       osp_results_t *results)
+{
+       OSP_DEBUG_START;
+
+       switch_log_printf(SWITCH_CHANNEL_LOG, osp_global.loglevel,
+               "AuthRsp: "
+               "destcount '%d/%d' "
+               "transid '%"PRIu64"' "
+               "timelimit '%u' "
+               "calling '%s' "
+               "called '%s' "
+               "destination '%s' "
+               "destnid '%s' "
+               "lnp '%s/%s/%d' "
+               "protocol '%s' "
+               "supported '%d'\n",
+               results->count,
+               results->total,
+               results->transid,
+               results->timelimit,
+               results->calling,
+               results->called,
+               results->dest,
+               results->destnid,
+               results->nprn, results->npcic, results->npdi,
+               osp_get_protocolname(results->protocol),
+               results->supported);
+
+       OSP_DEBUG_END;
+}
+
+/*
+ * Alway log UsageInd parameters
+ * param results Route info
+ * param usage Usage info
+ * return
+ */
+static void osp_log_usageind(
+       osp_results_t *results,
+       osp_usage_t *usage)
+{
+       OSP_DEBUG_START;
+
+       switch_log_printf(SWITCH_CHANNEL_LOG, osp_global.loglevel,
+               "UsageInd: "
+               "destcount '%d/%d' "
+               "transid '%"PRIu64"' "
+               "cause '%d' "
+               "release '%d' "
+               "times '%"PRId64"/%"PRId64"/%"PRId64"/%"PRId64"' "
+               "duration '%"PRId64"' "
+               "pdd '%"PRId64"' "
+               "codec '%s/%s' "
+               "rtpctets '%d/%d' "
+               "rtppackets '%d/%d'\n",
+               results->count, results->total,
+               results->transid,
+               results->cause ? results->cause : usage->cause,
+               usage->release,
+               usage->start / 1000000, usage->alert / 1000000, usage->connect / 1000000, usage->end / 1000000,
+               usage->duration / 1000000,
+               usage->pdd / 1000,
+               OSP_FILTER_NULLSTR(usage->srccodec), OSP_FILTER_NULLSTR(usage->destcodec),
+               usage->rtpsrcrepoctets, usage->rtpdestrepoctets,
+               usage->rtpsrcreppackets, usage->rtpdestreppackets);
+
+       OSP_DEBUG_END;
 }
 
 /*
@@ -1088,52 +1291,94 @@ static void osp_get_inbound(
        osp_inbound_t *inbound)
 {
        switch_caller_profile_t *caller;
-       switch_channel_timetable_t *times;
        const char *tmp;
        int i;
        char name[OSP_SIZE_NORSTR];
 
+       OSP_DEBUG_START;
+
+       /* Cleanup buffer */
        memset(inbound, 0, sizeof(*inbound));
 
+       /* Get caller profile */
        caller = switch_channel_get_caller_profile(channel);
 
+       /* osp_source_device */
        inbound->actsrc = switch_channel_get_variable(channel, OSP_VAR_SRCDEV);
+       OSP_DEBUG("actsrc: '%s'", OSP_FILTER_NULLSTR(inbound->actsrc));
+
+       /* Source device */
        inbound->srcdev = caller->network_addr;
-       inbound->protocol = osp_get_moduleprotocol(caller->source);
+       OSP_DEBUG("srcdev: '%s'", OSP_FILTER_NULLSTR(inbound->srcdev));
+
+       /* osp_source_nid */
+       inbound->srcnid = switch_channel_get_variable(channel, OSP_VAR_SRCNID);
+       OSP_DEBUG("srcnid: '%s'", OSP_FILTER_NULLSTR(inbound->srcnid));
+
+       /* Source signaling protocol */
+       inbound->protocol = osp_get_protocol(caller->source);
+       OSP_DEBUG("protocol: '%d'", inbound->protocol);
+
+       /* Call-ID */
+       inbound->callid = switch_channel_get_variable(channel, OSP_FS_CALLID);
+       OSP_DEBUG("callid: '%s'", OSP_FILTER_NULLSTR(inbound->callid));
+
+       /* Calling number */
        if ((tmp = switch_channel_get_variable(channel, OSP_FS_FROMUSER))) {
                osp_parse_userinfo(tmp, inbound->calling, sizeof(inbound->calling), NULL, 0, NULL, 0, NULL);
        } else {
                osp_parse_userinfo(caller->caller_id_number, inbound->calling, sizeof(inbound->calling), NULL, 0, NULL, 0, NULL);
        }
-       osp_parse_userinfo(caller->destination_number, inbound->called, sizeof(inbound->called), inbound->nprn, sizeof(inbound->nprn),
-               inbound->npcic, sizeof(inbound->npcic), &inbound->npdi);
+       OSP_DEBUG("calling: '%s'", inbound->calling);
 
+       /* Called number and LNP parameters */
+       osp_parse_userinfo(caller->destination_number, inbound->called, sizeof(inbound->called), inbound->nprn, sizeof(inbound->nprn), inbound->npcic, sizeof(inbound->npcic),
+               &inbound->npdi);
+       OSP_DEBUG("called: '%s'", inbound->called);
+       OSP_DEBUG("nprn: '%s'", inbound->nprn);
+       OSP_DEBUG("npcic: '%s'", inbound->npcic);
+       OSP_DEBUG("npdi: '%d'", inbound->npdi);
+
+       /* To header */
        inbound->tohost = switch_channel_get_variable(channel, OSP_FS_TOHOST);
+       OSP_DEBUG("tohost: '%s'", OSP_FILTER_NULLSTR(inbound->tohost));
        inbound->toport = switch_channel_get_variable(channel, OSP_FS_TOPORT);
+       OSP_DEBUG("toport: '%s'", OSP_FILTER_NULLSTR(inbound->toport));
 
+       /* RPID calling number */
        if ((tmp = switch_channel_get_variable(channel, OSP_FS_RPID))) {
                osp_parse_header_user(tmp, inbound->rpiduser, sizeof(inbound->rpiduser));
        }
+       OSP_DEBUG("RPID user: '%s'", inbound->rpiduser);
+
+       /* PAI calling number */
        if ((tmp = switch_channel_get_variable(channel, OSP_FS_PAI))) {
                osp_parse_userinfo(tmp, inbound->paiuser, sizeof(inbound->paiuser), NULL, 0, NULL, 0, NULL);
        }
+       OSP_DEBUG("PAI user: '%s'", inbound->paiuser);
+
+       /* DIV calling number */
        if ((tmp = switch_channel_get_variable(channel, OSP_FS_DIV))) {
                osp_parse_header_user(tmp, inbound->divuser, sizeof(inbound->divuser));
                osp_parse_header_host(tmp, inbound->divhost, sizeof(inbound->divhost));
        }
+       OSP_DEBUG("DIV user: '%s'", inbound->divuser);
+       OSP_DEBUG("DIV host: '%s'", inbound->divhost);
+
+       /* PCI calling number */
        if ((tmp = switch_channel_get_variable(channel, OSP_FS_PCI))) {
                osp_parse_header_user(tmp, inbound->pciuser, sizeof(inbound->pciuser));
        }
+       OSP_DEBUG("PCI user: '%s'", inbound->pciuser);
 
-       inbound->srcnid = switch_channel_get_variable(channel, OSP_VAR_SRCNID);
-
-       times = switch_channel_get_timetable(channel);
-       inbound->start = times->created;
-
+       /* Custom info */
        for (i = 0; i < OSP_MAX_CINFO; i++) {
                switch_snprintf(name, sizeof(name), "%s%d", OSP_VAR_CUSTOMINFO, i + 1);
                inbound->cinfo[i] = switch_channel_get_variable(channel, name);
+               OSP_DEBUG("cinfo[%d]: '%s'", i, OSP_FILTER_NULLSTR(inbound->cinfo[i]));
        }
+
+       OSP_DEBUG_END;
 }
 
 /*
@@ -1146,119 +1391,211 @@ static void osp_get_outbound(
        switch_channel_t *channel,
        osp_outbound_t *outbound)
 {
-       const char *value;
+       const char *tmp;
+
+       OSP_DEBUG_START;
 
+       /* Cleanup buffer */
        memset(outbound, 0, sizeof(*outbound));
 
        /* Get destination network ID namd & location info */
        outbound->dniduserparam = switch_channel_get_variable(channel, OSP_VAR_DNIDUSERPARAM);
+       OSP_DEBUG("dniduserparam: '%s'", OSP_FILTER_NULLSTR(outbound->dniduserparam));
        outbound->dniduriparam = switch_channel_get_variable(channel, OSP_VAR_DNIDURIPARAM);
+       OSP_DEBUG("dniduriparam: '%s'", OSP_FILTER_NULLSTR(outbound->dniduriparam));
 
        /* Get "user=phone" insert flag */
-       value = switch_channel_get_variable(channel, OSP_VAR_USERPHONE);
-       if (!switch_strlen_zero(value)) {
-               outbound->userphone = switch_true(value);
+       tmp = switch_channel_get_variable(channel, OSP_VAR_USERPHONE);
+       if (!switch_strlen_zero(tmp)) {
+               outbound->userphone = switch_true(tmp);
        }
+       OSP_DEBUG("userphone: '%d'", outbound->userphone);
 
        /* Get outbound proxy info */
        outbound->outproxy = switch_channel_get_variable(channel, OSP_VAR_OUTPROXY);
+       OSP_DEBUG("outporxy: '%s'", OSP_FILTER_NULLSTR(outbound->outproxy));
+
+       OSP_DEBUG_END;
 }
 
 /*
- * Convert "address:port" to "[x.x.x.x]:port" or "hostname:port" format
- * param src Source address string
- * param dest Destination address string
- * param destsize Size of dest buffer
- * return
+ * Get transaction info
+ * param channel Inbound channel
+ * param aleg If A-leg
+ * param results Route info
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
  */
-static void osp_convert_inout(
-       const char *src,
-       char *dest,
-       int destsize)
+static switch_status_t osp_get_transaction(
+       switch_channel_t *channel,
+       switch_bool_t aleg,
+       osp_results_t *results)
 {
-       struct in_addr inp;
-       char buffer[OSP_SIZE_NORSTR];
-       char *port;
+       const char *tmp;
+       osp_profile_t *profile;
+       switch_status_t status = SWITCH_STATUS_FALSE;
 
-       if (dest && (destsize > 0)) {
-               if (!switch_strlen_zero(src)) {
-                       switch_copy_string(buffer, src, sizeof(buffer));
+       OSP_DEBUG_START;
 
-                       if((port = strchr(buffer, ':'))) {
-                               *port = '\0';
-                               port++;
-                       }
+       /* Get profile name */
+       if (!(results->profile = switch_channel_get_variable(channel, OSP_VAR_PROFILE))) {
+               results->profile = OSP_DEF_PROFILE;
+       }
 
-                       if (inet_pton(AF_INET, buffer, &inp) == 1) {
-                               if (port) {
-                                       switch_snprintf(dest, destsize, "[%s]:%s", buffer, port);
-                               } else {
-                                       switch_snprintf(dest, destsize, "[%s]", buffer);
-                               }
-                               dest[destsize - 1] = '\0';
-                       } else {
-                               switch_copy_string(dest, src, destsize);
+       /* Get transaction handle */
+       if (osp_find_profile(results->profile, &profile) == SWITCH_STATUS_FALSE) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to find profile '%s'\n", results->profile);
+       } else if (profile->provider == OSP_INVALID_HANDLE) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Disabled profile '%s'\n", results->profile);
+       } else if (!(tmp = switch_channel_get_variable(channel, OSP_VAR_TRANSACTION)) || (sscanf(tmp, "%d", &results->transaction) != 1)){
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get transaction handle'\n");
+       } else if (results->transaction == OSP_INVALID_HANDLE) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid transaction handle'\n");
+       } else {
+               if (!(tmp = switch_channel_get_variable(channel, OSP_VAR_TRANSID)) || (sscanf(tmp, "%"PRIu64"", &results->transid) != 1)) {
+                       results->transid = 0;
+               }
+
+               /* Get destination total */
+               if (!(tmp = switch_channel_get_variable(channel, OSP_VAR_ROUTETOTAL)) || (sscanf(tmp, "%u", &results->total) != 1)) {
+                       results->total = 0;
+               }
+
+               /* Get destination count */
+               if (!(tmp = switch_channel_get_variable(channel, OSP_VAR_ROUTECOUNT)) || (sscanf(tmp, "%u", &results->count) != 1)) {
+                       results->count = 0;
+               }
+
+               /*
+                * Get termiantion cause
+                * The logic is
+                * 1. osp_next_function should get TCCode from last_bridge_hangup_cause
+                * 2. osp_on_reporting should get TCCode from osp_terimation_cause
+                */
+               if (aleg) {
+                       /* A-leg */
+                       if (!(tmp = switch_channel_get_variable(channel, OSP_VAR_TCCODE)) || (sscanf(tmp, "%u", &results->cause) != 1)) {
+                               results->cause = 0;
                        }
                } else {
-                       *dest = '\0';
+                       /* B-leg */
+                       if ((tmp = switch_channel_get_variable(channel, OSP_FS_HANGUPCAUSE))) {
+                               results->cause = switch_channel_str2cause(tmp);
+                       }
                }
+
+               status = SWITCH_STATUS_SUCCESS;
        }
+       OSP_DEBUG("profile: '%s'", results->profile);
+       OSP_DEBUG("transaction: '%d'", results->transaction);
+       OSP_DEBUG("transid: '%"PRIu64"'", results->transid);
+       OSP_DEBUG("total: '%d'", results->total);
+       OSP_DEBUG("count: '%d'", results->count);
+       OSP_DEBUG("cause: '%d'", results->cause);
+
+       OSP_DEBUG_END;
+
+       return status;
 }
 
 /*
- * Convert "[x.x.x.x]:port" or "hostname:prot" to "address:port" format
- * param src Source address string
- * param dest Destination address string
- * param destsize Size of dest buffer
+ * Retrieve usage info
+ * param channel channel
+ * param originator Originator profile
+ * param terminator Terminator profile, not used at this time
+ * param results Route info
+ * param usage Usage info
  * return
  */
-static void osp_convert_outin(
-       const char *src,
-       char *dest,
-       int destsize)
+static void osp_get_usage(
+       switch_channel_t *channel,
+       switch_caller_profile_t *originator,
+       switch_caller_profile_t *terminator_unused,
+       osp_results_t *results,
+       osp_usage_t *usage)
 {
-       char buffer[OSP_SIZE_NORSTR];
-       char *end;
-       char *port;
-
-       if (dest && (destsize > 0)) {
-               if (!switch_strlen_zero(src)) {
-                       switch_copy_string(buffer, src, sizeof(buffer));
+       const char *tmp;
+       switch_channel_timetable_t *times;
 
-                       if (buffer[0] == '[') {
-                               if((port = strchr(buffer + 1, ':'))) {
-                                       *port = '\0';
-                                       port++;
-                               }
+       OSP_DEBUG_START;
 
-                               if ((end = strchr(buffer + 1, ']'))) {
-                                       *end = '\0';
-                               }
+       /* Cleanup buffer */
+       memset(usage, 0, sizeof(*usage));
 
-                               if (port) {
-                                       switch_snprintf(dest, destsize, "%s:%s", buffer + 1, port);
-                                       dest[destsize - 1] = '\0';
-                               } else {
-                                       switch_copy_string(dest, buffer + 1, destsize);
-                               }
-                       } else {
-                               switch_copy_string(dest, src, destsize);
-                       }
-               } else {
-                       *dest = '\0';
+       /* Release source */
+       usage->release = OSPC_RELEASE_UNKNOWN;
+       if (osp_get_protocol(originator->source) == OSPC_PROTNAME_SIP) {
+               tmp = switch_channel_get_variable(channel, OSP_FS_SIPRELEASE);
+               if (!tmp) {
+                       usage->release = OSPC_RELEASE_UNDEFINED;
+               } else if (!strcasecmp(tmp, "send_bye")) {
+                       usage->release = OSPC_RELEASE_DESTINATION;
+               } else if (!strcasecmp(tmp, "recv_bye")) {
+                       usage->release = OSPC_RELEASE_SOURCE;
+               } else if (!strcasecmp(tmp, "send_refuse")) {
+                       usage->release = OSPC_RELEASE_INTERNAL;
+               } else if (!strcasecmp(tmp, "send_cancel")) {
+                       usage->release = OSPC_RELEASE_SOURCE;
                }
        }
+       OSP_DEBUG("release: '%d'", usage->release);
+
+       /* Termiation cause */
+       usage->cause = switch_channel_get_cause_q850(channel);
+       OSP_DEBUG("cause: '%d'", usage->cause);
+
+       /* Timestamps */
+       times = switch_channel_get_timetable(channel);
+       usage->start = times->created;
+       OSP_DEBUG("start: '%"PRIu64"'", usage->start);
+       usage->alert = times->progress;
+       OSP_DEBUG("alert: '%"PRIu64"'", usage->alert);
+       usage->connect = times->answered;
+       OSP_DEBUG("connect: '%"PRIu64"'", usage->connect);
+       usage->end = times->hungup;
+       OSP_DEBUG("end: '%"PRIu64"'", usage->end);
+       if (times->answered) {
+               usage->duration = times->hungup - times->answered;
+               OSP_DEBUG("duration: '%"PRIu64"'", usage->duration);
+       }
+       if (times->progress) {
+               usage->pdd = times->progress - usage->start;
+               OSP_DEBUG("pdd: '%"PRIu64"'", usage->pdd);
+       }
+
+       /* Codecs */
+       usage->srccodec = switch_channel_get_variable(channel, OSP_FS_SRCCODEC);
+       OSP_DEBUG("srccodec: '%s'", OSP_FILTER_NULLSTR(usage->srccodec));
+       usage->destcodec = switch_channel_get_variable(channel, OSP_FS_DESTCODEC);
+       OSP_DEBUG("destcodec: '%s'", OSP_FILTER_NULLSTR(usage->destcodec));
+
+       /* QoS statistics */
+       if (!(tmp = switch_channel_get_variable(channel, OSP_FS_RTPSRCREPOCTS)) || (sscanf(tmp, "%d", &usage->rtpsrcrepoctets) != 1)) {
+               usage->rtpsrcrepoctets = OSP_DEF_STATS;
+       }
+       OSP_DEBUG("rtpsrcrepoctets: '%d'", usage->rtpsrcrepoctets);
+       if (!(tmp = switch_channel_get_variable(channel, OSP_FS_RTPDESTREPOCTS)) || (sscanf(tmp, "%d", &usage->rtpdestrepoctets) != 1)) {
+               usage->rtpdestrepoctets = OSP_DEF_STATS;
+       }
+       OSP_DEBUG("rtpdestrepoctets: '%d'", usage->rtpdestrepoctets);
+       if (!(tmp = switch_channel_get_variable(channel, OSP_FS_RTPSRCREPPKTS)) || (sscanf(tmp, "%d", &usage->rtpsrcreppackets) != 1)) {
+               usage->rtpsrcreppackets = OSP_DEF_STATS;
+       }
+       OSP_DEBUG("rtpsrcreppackets: '%d'", usage->rtpsrcreppackets);
+       if (!(tmp = switch_channel_get_variable(channel, OSP_FS_RTPDESTREPPKTS)) || (sscanf(tmp, "%d", &usage->rtpdestreppackets) != 1)) {
+               usage->rtpdestreppackets = OSP_DEF_STATS;
+       }
+       OSP_DEBUG("rtpdestreppackets: '%d'", usage->rtpdestreppackets);
+
+       OSP_DEBUG_END;
 }
 
 /*
  * Check destination
- * param transaction Transaction handle
- * param dest Destination
+ * param results Routing info
  * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
  */
 static switch_status_t osp_check_destination(
-       OSPTTRANHANDLE transaction,
-       osp_destination_t *dest)
+       osp_results_t *results)
 {
        OSPE_DEST_OSPENABLED enabled;
        OSPE_PROTOCOL_NAME protocol;
@@ -1266,159 +1603,289 @@ static switch_status_t osp_check_destination(
        int error;
        switch_status_t status = SWITCH_STATUS_FALSE;
 
-       if ((transaction == OSP_INVALID_HANDLE) || !dest) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid parameters\n");
-               return status;
-       }
-
-       dest->supported = SWITCH_FALSE;
+       OSP_DEBUG_START;
 
-       if ((error = OSPPTransactionIsDestOSPEnabled(transaction, &enabled)) != OSPC_ERR_NO_ERROR) {
+       if ((error = OSPPTransactionIsDestOSPEnabled(results->transaction, &enabled)) != OSPC_ERR_NO_ERROR) {
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get destination OSP version, error '%d'\n", error);
-               return status;
-       }
-
-       if ((error = OSPPTransactionGetDestProtocol(transaction, &protocol)) != OSPC_ERR_NO_ERROR) {
+       } else if ((error = OSPPTransactionGetDestProtocol(results->transaction, &protocol)) != OSPC_ERR_NO_ERROR) {
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get signaling protocol, error '%d'\n", error);
-               return status;
-       }
-
-       switch(protocol) {
-       case OSPC_PROTNAME_UNDEFINED:
-       case OSPC_PROTNAME_UNKNOWN:
-               protocol = osp_globals.protocol;
-       case OSPC_PROTNAME_SIP:
-       case OSPC_PROTNAME_Q931:
-       case OSPC_PROTNAME_IAX:
-       case OSPC_PROTNAME_SKYPE:
-               dest->protocol = protocol;
-               if (!switch_strlen_zero(osp_globals.modules[protocol]) && !switch_strlen_zero(osp_globals.profiles[protocol])) {
-                       dest->supported = SWITCH_TRUE;
-                       status = SWITCH_STATUS_SUCCESS;
+       } else {
+               switch(protocol) {
+               case OSPC_PROTNAME_UNDEFINED:
+               case OSPC_PROTNAME_UNKNOWN:
+                       protocol = osp_global.protocol;
+               case OSPC_PROTNAME_SIP:
+               case OSPC_PROTNAME_Q931:
+               case OSPC_PROTNAME_IAX:
+               case OSPC_PROTNAME_SKYPE:
+                       results->protocol = protocol;
+                       if (!switch_strlen_zero(osp_global.endpoint[protocol].module) && !switch_strlen_zero(osp_global.endpoint[protocol].profile)) {
+                               results->supported = SWITCH_TRUE;
+                               results->cause = 0;
+                               status = SWITCH_STATUS_SUCCESS;
+                               break;
+                       }
+               case OSPC_PROTNAME_LRQ:
+               case OSPC_PROTNAME_T37:
+               case OSPC_PROTNAME_T38:
+               case OSPC_PROTNAME_SMPP:
+               case OSPC_PROTNAME_XMPP:
+               default:
+                       results->protocol = protocol;
+                       results->supported = SWITCH_FALSE;
+                       /* Q.850 protocol error, unspecified */
+                       results->cause = 111;
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported protocol '%d'\n", protocol);
+                       break;
+               }
+               OSP_DEBUG("protocol: '%d'", results->protocol);
+               OSP_DEBUG("supported: '%d'", results->supported);
+               OSP_DEBUG("cause: '%d'", results->cause);
+
+               if ((error = OSPPTransactionGetDestinationNetworkId(results->transaction, sizeof(results->destnid), results->destnid)) != OSPC_ERR_NO_ERROR) {
+                       results->destnid[0] = '\0';
+               }
+               OSP_DEBUG("destnid: '%s'", results->destnid);
+
+               error = OSPPTransactionGetNumberPortabilityParameters(
+                       results->transaction,
+                       sizeof(results->nprn),
+                       results->nprn,
+                       sizeof(results->npcic),
+                       results->npcic,
+                       &results->npdi);
+               if (error != OSPC_ERR_NO_ERROR) {
+                       results->nprn[0] = '\0';
+                       results->npcic[0] = '\0';
+                       results->npdi = 0;
+               }
+               OSP_DEBUG("nprn: '%s'", results->nprn);
+               OSP_DEBUG("npcic: '%s'", results->npcic);
+               OSP_DEBUG("npdi: '%d'", results->npdi);
+
+               for (type = OSPC_OPNAME_START; type < OSPC_OPNAME_NUMBER; type++) {
+                       if ((error = OSPPTransactionGetOperatorName(results->transaction, type, sizeof(results->opname[type]), results->opname[type])) != OSPC_ERR_NO_ERROR) {
+                               results->opname[type][0] = '\0';
+                       }
+                       OSP_DEBUG("opname[%d]: '%s'", type, results->opname[type]);
                }
-               break;
-       case OSPC_PROTNAME_LRQ:
-       case OSPC_PROTNAME_T37:
-       case OSPC_PROTNAME_T38:
-       case OSPC_PROTNAME_SMPP:
-       case OSPC_PROTNAME_XMPP:
-       default:
-               dest->protocol = protocol;
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unsupported protocol '%d'\n", protocol);
-               break;
-       }
 
-       if ((error = OSPPTransactionGetDestinationNetworkId(transaction, sizeof(dest->destnid), dest->destnid)) != OSPC_ERR_NO_ERROR) {
-               dest->destnid[0] = '\0';
+               osp_log_authrsp(results);
        }
 
-       error = OSPPTransactionGetNumberPortabilityParameters(
-               transaction,
-               sizeof(dest->nprn),
-               dest->nprn,
-               sizeof(dest->npcic),
-               dest->npcic,
-               &dest->npdi);
-       if (error != OSPC_ERR_NO_ERROR) {
-               dest->nprn[0] = '\0';
-               dest->npcic[0] = '\0';
-               dest->npdi = 0;
+       OSP_DEBUG_END;
+
+       return status;
+}
+
+/*
+ * Build parameter string for each channel
+ * param results Route info
+ * param buffer Buffer
+ * param bufsize Buffer size
+ * return
+ */
+static void osp_build_eachparam(
+       osp_results_t *results,
+       char *buffer,
+       switch_size_t bufsize)
+{
+       OSP_DEBUG_START;
+
+       if (results && buffer && bufsize) {
+               switch_snprintf(buffer, bufsize, "[%s=%s]", OSP_FS_OUTCALLING, results->calling);
+               OSP_DEBUG("eachparam: '%s'", buffer);
        }
 
-       for (type = OSPC_OPNAME_START; type < OSPC_OPNAME_NUMBER; type++) {
-               if ((error = OSPPTransactionGetOperatorName(transaction, type, sizeof(dest->opname[type]), dest->opname[type])) != OSPC_ERR_NO_ERROR) {
-                       dest->opname[type][0] = '\0';
+       OSP_DEBUG_END;
+}
+
+/*
+ * Build endpoint string
+ * param results Route info
+ * param outbound Outbound settings
+ * param buffer Buffer
+ * param bufsize Buffer size
+ * return
+ */
+static void osp_build_endpoint(
+       osp_results_t *results,
+       osp_outbound_t *outbound,
+       char *buffer,
+       switch_size_t bufsize)
+{
+       char *head = buffer;
+       switch_size_t len, size = bufsize;
+
+       OSP_DEBUG_START;
+
+       if (results && buffer && bufsize) {
+               switch (results->protocol) {
+               case OSPC_PROTNAME_SIP:
+                       /* module/profile/called */
+                       switch_snprintf(head, size, "%s/%s/%s", osp_global.endpoint[OSPC_PROTNAME_SIP].module, osp_global.endpoint[OSPC_PROTNAME_SIP].profile, results->called);
+                       OSP_ADJUST_LEN(head, size, len);
+
+                       /* RN */
+                       if (!switch_strlen_zero_buf(results->nprn)) {
+                               switch_snprintf(head, size, ";rn=%s", results->nprn);
+                               OSP_ADJUST_LEN(head, size, len);
+                       }
+
+                       /* CIC */
+                       if (!switch_strlen_zero_buf(results->npcic)) {
+                               switch_snprintf(head, size, ";cic=%s", results->npcic);
+                               OSP_ADJUST_LEN(head, size, len);
+                       }
+
+                       /* NPDI */
+                       if (results->npdi) {
+                               switch_snprintf(head, size, ";npdi");
+                               OSP_ADJUST_LEN(head, size, len);
+                       }
+
+                       /* User parameter destination network ID */
+                       if (!switch_strlen_zero(outbound->dniduserparam) && !switch_strlen_zero_buf(results->destnid)) {
+                               switch_snprintf(head, size, ";%s=%s", outbound->dniduserparam, results->destnid);
+                               OSP_ADJUST_LEN(head, size, len);
+                       }
+
+                       /* Destination */
+                       switch_snprintf(head, size, "@%s", results->dest);
+                       OSP_ADJUST_LEN(head, size, len);
+
+                       /* URI parameter destination network ID */
+                       if (!switch_strlen_zero(outbound->dniduriparam) && !switch_strlen_zero_buf(results->destnid)) {
+                               switch_snprintf(head, size, ";%s=%s", outbound->dniduriparam, results->destnid);
+                               OSP_ADJUST_LEN(head, size, len);
+                       }
+
+                       /* user=phone */
+                       if (outbound->userphone) {
+                               switch_snprintf(head, size, ";user=phone");
+                               OSP_ADJUST_LEN(head, size, len);
+                       }
+
+                       /* Outbound proxy */
+                       if (!switch_strlen_zero(outbound->outproxy)) {
+                               switch_snprintf(head, size, ";fs_path=sip:%s", outbound->outproxy);
+                               OSP_ADJUST_LEN(head, size, len);
+                       }
+                       break;
+               case OSPC_PROTNAME_Q931:
+                       /* module/profile/called@destination */
+                       switch_snprintf(head, size, "%s/%s/%s@%s", osp_global.endpoint[OSPC_PROTNAME_Q931].module, osp_global.endpoint[OSPC_PROTNAME_Q931].profile,
+                               results->called, results->dest);
+                       OSP_ADJUST_LEN(head, size, len);
+                       break;
+               case OSPC_PROTNAME_IAX:
+                       /* module/profile/destination/called */
+                       switch_snprintf(head, size, "%s/%s/%s/%s", osp_global.endpoint[OSPC_PROTNAME_Q931].module, osp_global.endpoint[OSPC_PROTNAME_Q931].profile,
+                               results->dest, results->called);
+                       OSP_ADJUST_LEN(head, size, len);
+                       break;
+               case OSPC_PROTNAME_SKYPE:
+                       /* module/profile/called */
+                       switch_snprintf(head, size, "%s/%s/%s", osp_global.endpoint[OSPC_PROTNAME_Q931].module, osp_global.endpoint[OSPC_PROTNAME_Q931].profile, results->called);
+                       OSP_ADJUST_LEN(head, size, len);
+                       break;
+               default:
+                       buffer[0] = '\0';
+                       break;
                }
+               OSP_DEBUG("endpoint: '%s'", buffer);
        }
 
-       return status;
+       OSP_DEBUG_END;
 }
 
 /*
- * Log AuthRsp parameters
+ * Create route string
+ * param outbound Outbound info
  * param results Routing info
+ * param buffer Buffer
+ * param bufsize Buffer size
  * return
  */
-static void osp_log_authrsp(
-       osp_results_t *results)
+static void osp_create_route(
+       osp_outbound_t *outbound,
+       osp_results_t *results,
+       char *buffer,
+       switch_size_t bufsize)
 {
-       int i;
+       char eachparam[OSP_SIZE_NORSTR];
+       char endpoint[OSP_SIZE_NORSTR];
 
-       if (osp_globals.debug) {
-               for (i = 0; i < results->numdest; i++) {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, osp_globals.loglevel,
-                               "AuthRsp: "
-                               "transid '%"PRIu64"' "
-                               "destcount '%d' "
-                               "timelimit '%u' "
-                               "destination '%s' "
-                               "calling '%s' "
-                               "called '%s' "
-                               "destnid '%s' "
-                               "lnp '%s/%s/%d' "
-                               "protocol '%s' "
-                               "supported '%d'\n",
-                               results->transid,
-                               i + 1,
-                               results->dests[i].timelimit,
-                               results->dests[i].dest,
-                               results->dests[i].calling,
-                               results->dests[i].called,
-                               results->dests[i].destnid,
-                               results->dests[i].nprn, results->dests[i].npcic, results->dests[i].npdi,
-                               osp_get_protocol(results->dests[i].protocol),
-                               results->dests[i].supported);
-               }
-       }
+       OSP_DEBUG_START;
+
+       /* Build dial string for each channel part */
+       osp_build_eachparam(results, eachparam, sizeof(eachparam));
+
+       /* Build dial string for endpoint part */
+       osp_build_endpoint(results, outbound, endpoint, sizeof(endpoint));
+
+       /* Build dail string */
+       switch_snprintf(buffer, bufsize, "%s%s", eachparam, endpoint);
+       OSP_DEBUG("route: '%s'", buffer);
+
+       OSP_DEBUG_END;
 }
 
 /*
- * Do auth/routing request
+ * Do AuthReq
  * param profile OSP profile
- * param transaction Transaction handle
  * param inbound Call originator info
  * param results Routing info
  * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
  */
-static switch_status_t osp_do_request(
+static switch_status_t osp_request_auth(
        osp_profile_t *profile,
-       OSPTTRANHANDLE transaction,
        osp_inbound_t *inbound,
        osp_results_t *results)
 {
-       OSPTTRANS *context;
-       osp_destination_t *dest;
-       char tmp[OSP_SIZE_NORSTR];
        const char *source;
        const char *srcdev;
+       char tmp[OSP_SIZE_NORSTR];
        char src[OSP_SIZE_NORSTR];
        char dev[OSP_SIZE_NORSTR];
        char term[OSP_SIZE_NORSTR];
        const char *preferred[2] = { NULL };
-       unsigned int callidlen = 0, tokenlen = 0, total;
-       int count, error;
+       OSPTTRANS *context;
+       int i, error;
        switch_status_t status = SWITCH_STATUS_FALSE;
 
-       osp_log_authreq(profile, inbound);
+       OSP_DEBUG_START;
+
+       /* Set source network ID */
+       OSPPTransactionSetNetworkIds(results->transaction, inbound->srcnid, NULL);
+
+       /* Set source signaling protocol */
+       OSPPTransactionSetProtocol(results->transaction, OSPC_PROTTYPE_SOURCE, inbound->protocol);
 
-       OSPPTransactionSetProtocol(transaction, OSPC_PROTTYPE_SOURCE, inbound->protocol);
+       /* Set source LNP parameters */
+       OSPPTransactionSetNumberPortability(results->transaction, inbound->nprn, inbound->npcic, inbound->npdi);
 
-       OSPPTransactionSetNumberPortability(transaction, inbound->nprn, inbound->npcic, inbound->npdi);
+       /* Set RPID */
+       OSPPTransactionSetRemotePartyId(results->transaction, OSPC_NFORMAT_E164, inbound->rpiduser);
 
-       OSPPTransactionSetRemotePartyId(transaction, OSPC_NFORMAT_E164, inbound->rpiduser);
-       OSPPTransactionSetAssertedId(transaction, OSPC_NFORMAT_E164, inbound->paiuser);
+       /* Set PAI */
+       OSPPTransactionSetAssertedId(results->transaction, OSPC_NFORMAT_E164, inbound->paiuser);
+
+       /* Set diversion */
        osp_convert_inout(inbound->divhost, tmp, sizeof(tmp));
-       OSPPTransactionSetDiversion(transaction, inbound->divuser, tmp);
-       OSPPTransactionSetChargeInfo(transaction, OSPC_NFORMAT_E164, inbound->pciuser);
+       OSPPTransactionSetDiversion(results->transaction, inbound->divuser, tmp);
 
-       OSPPTransactionSetNetworkIds(transaction, inbound->srcnid, NULL);
+       /* Set PCI */
+       OSPPTransactionSetChargeInfo(results->transaction, OSPC_NFORMAT_E164, inbound->pciuser);
 
-       for (count = 0; count < OSP_MAX_CINFO; count++) {
-               if (!switch_strlen_zero(inbound->cinfo[count])) {
-                       OSPPTransactionSetCustomInfo(transaction, count, inbound->cinfo[count]);
+       /* Set custom info */
+       for (i = 0; i < OSP_MAX_CINFO; i++) {
+               if (!switch_strlen_zero(inbound->cinfo[i])) {
+                       OSPPTransactionSetCustomInfo(results->transaction, i, inbound->cinfo[i]);
                }
        }
 
+       /* Device info and source */
        if (profile->workmode == OSP_MODE_INDIRECT) {
                source = inbound->srcdev;
                if (switch_strlen_zero(inbound->actsrc)) {
@@ -1427,14 +1894,15 @@ static switch_status_t osp_do_request(
                        srcdev = inbound->actsrc;
                }
        } else {
-               source = profile->device;
+               source = profile->deviceip;
                srcdev = inbound->srcdev;
        }
        osp_convert_inout(source, src, sizeof(src));
        osp_convert_inout(srcdev, dev, sizeof(dev));
 
+       /* Preferred and max destinations */
        if (profile->srvtype == OSP_SRV_NPQUERY) {
-               OSPPTransactionSetServiceType(transaction, OSPC_SERVICE_NPQUERY);
+               OSPPTransactionSetServiceType(results->transaction, OSPC_SERVICE_NPQUERY);
 
                if (switch_strlen_zero(inbound->tohost)) {
                        switch_copy_string(term, src, sizeof(term));
@@ -1448,925 +1916,689 @@ static switch_status_t osp_do_request(
                }
                preferred[0] = term;
 
-               total = 1;
+               results->total = 1;
        } else {
-               OSPPTransactionSetServiceType(transaction, OSPC_SERVICE_VOICE);
+               OSPPTransactionSetServiceType(results->transaction, OSPC_SERVICE_VOICE);
 
-               total = profile->maxdest;
+               results->total = profile->maxdest;
        }
 
+       OSP_DEBUG_MSG("RequestAuthorisation");
+
+       /* Request authorization */
        error = OSPPTransactionRequestAuthorisation(
-               transaction,            /* Transaction handle */
-               src,                            /* Source */
-               dev,                            /* Source device */
-               inbound->calling,       /* Calling */
-               OSPC_NFORMAT_E164,      /* Calling format */
-               inbound->called,        /* Called */
-               OSPC_NFORMAT_E164,      /* Called format */
-               NULL,                           /* User */
-               0,                                      /* Number of callids */
-               NULL,                           /* Callids */
-               preferred,                      /* Preferred destinations */
-               &total,                         /* Destination number */
-               NULL,                           /* Log buffer size */
-               NULL);                          /* Log buffer */
+               results->transaction,   /* Transaction handle */
+               src,                                    /* Source */
+               dev,                                    /* Source device */
+               inbound->calling,               /* Calling */
+               OSPC_NFORMAT_E164,              /* Calling format */
+               inbound->called,                /* Called */
+               OSPC_NFORMAT_E164,              /* Called format */
+               NULL,                                   /* User */
+               0,                                              /* Number of callids */
+               NULL,                                   /* Callids */
+               preferred,                              /* Preferred destinations */
+               &results->total,                /* Destination number */
+               NULL,                                   /* Log buffer size */
+               NULL);                                  /* Log buffer */
        if (error != OSPC_ERR_NO_ERROR) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to request routing for '%s/%s', error '%d'\n",
-                       inbound->calling, inbound->called, error);
-               results->status = error;
-               results->numdest = 0;
-               return status;
-       } else if (!total) {
-               results->status = error;
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unable to request routing for '%s/%s', error '%d'\n", inbound->calling, inbound->called, error);
+               results->total = 0;
+       } else if (results->total == 0) {
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Without destination\n");
-               results->status = error;
-               results->numdest = 0;
-               return status;
-       }
-
-       context = OSPPTransactionGetContext(transaction, &error);
-       if (error == OSPC_ERR_NO_ERROR) {
-               results->transid = context->TransactionID;
        } else {
-               results->transid = 0;
-       }
-
-       switch_copy_string(results->calling, inbound->calling, sizeof(results->calling));
-       switch_copy_string(results->called, inbound->called, sizeof(results->called));
-       results->srcdev = srcdev;
-       results->srcnid = inbound->srcnid;
-       results->start = inbound->start;
-
-       count = 0;
-       dest = &results->dests[count];
-       error = OSPPTransactionGetFirstDestination(
-               transaction,                    /* Transaction handle */
-               0,                                              /* Timestamp buffer size */
-               NULL,                                   /* Valid after */
-               NULL,                                   /* Valid until */
-               &dest->timelimit,               /* Call duration limit */
-               &callidlen,                             /* Callid buffer size */
-               NULL,                                   /* Callid buffer */
-               sizeof(dest->called),   /* Called buffer size */
-               dest->called,                   /* Called buffer */
-               sizeof(dest->calling),  /* Calling buffer size */
-               dest->calling,                  /* Calling buffer */
-               sizeof(term),                   /* Destination buffer size */
-               term,                                   /* Destination buffer */
-               0,                                              /* Destination device buffer size */
-               NULL,                                   /* Destination device buffer */
-               &tokenlen,                              /* Token buffer length */
-               NULL);                                  /* Token buffer */
-       if (error != OSPC_ERR_NO_ERROR) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get first destination, error '%d'\n", error);
-               results->status = error;
-               results->numdest = 0;
-               return status;
-       }
-
-       osp_convert_outin(term, dest->dest, sizeof(dest->dest));
-       osp_check_destination(transaction, dest);
-
-       for (count = 1; count < total; count++) {
-               dest = &results->dests[count];
-               error = OSPPTransactionGetNextDestination(
-                       transaction,                    /* Transsaction handle */
-                       OSPC_FAIL_NONE,                 /* Failure reason */
-                       0,                                              /* Timestamp buffer size */
-                       NULL,                                   /* Valid after */
-                       NULL,                                   /* Valid until */
-                       &dest->timelimit,               /* Call duration limit */
-                       &callidlen,                             /* Callid buffer size */
-                       NULL,                                   /* Callid buffer */
-                       sizeof(dest->called),   /* Called buffer size */
-                       dest->called,                   /* Called buffer */
-                       sizeof(dest->calling),  /* Calling buffer size */
-                       dest->calling,                  /* Calling buffer */
-                       sizeof(term),                   /* Destination buffer size */
-                       term,                                   /* Destination buffer */
-                       0,                                              /* Destination device buffer size */
-                       NULL,                                   /* Destination device buffer */
-                       &tokenlen,                              /* Token buffer length */
-                       NULL);                                  /* Token buffer */
+               context = OSPPTransactionGetContext(results->transaction, &error);
                if (error == OSPC_ERR_NO_ERROR) {
-                       osp_convert_outin(term, dest->dest, sizeof(dest->dest));
-                       osp_check_destination(transaction, dest);
+                       results->transid = context->TransactionID;
                } else {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get destination, error '%d'\n", error);
-                       break;
+                       results->transid = 0;
                }
-       }
-       if (count == total) {
-               results->status = OSPC_ERR_NO_ERROR;
-               results->numdest = total;
-               osp_log_authrsp(results);
                status = SWITCH_STATUS_SUCCESS;
-       } else {
-               results->status = error;
-               results->numdest = 0;
        }
+       OSP_DEBUG("transid: '%"PRIu64"'", results->transid);
+       OSP_DEBUG("total: '%d'", results->total);
+
+       OSP_DEBUG_END;
 
        return status;
 }
 
 /*
- * Request auth/routing info
- * param channel Originator channel
- * param profile Profile name
+ * Get first destination
  * param results Routing info
  * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
  */
-static switch_status_t osp_request_routing(
-       switch_channel_t *channel,
-       const char *profilename,
+static switch_status_t osp_get_first(
        osp_results_t *results)
 {
-       osp_profile_t *profile;
-       OSPTTRANHANDLE transaction;
-       osp_inbound_t inbound;
        int error;
+       char term[OSP_SIZE_NORSTR];
+       unsigned int callidlen = 0, tokenlen = 0;
        switch_status_t status = SWITCH_STATUS_FALSE;
 
-       if (osp_find_profile(profilename, &profile) != SWITCH_STATUS_SUCCESS) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to find profile '%s'\n", profilename);
-               return status;
-       }
-
-       if (profile->provider == OSP_INVALID_HANDLE) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Disabled profile '%s'\n", profilename);
-               return status;
-       }
+       OSP_DEBUG_START;
 
-       results->profile = profilename;
+       /* Set destination count */
+       results->count = 1;
 
-       if ((error = OSPPTransactionNew(profile->provider, &transaction)) != OSPC_ERR_NO_ERROR) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create transaction handle, error '%d'\n", error);
-               return status;
-       }
+       OSP_DEBUG_MSG("GetFirstDestination");
 
-       osp_get_inbound(channel, &inbound);
+       /* Get first destination */
+       error = OSPPTransactionGetFirstDestination(
+               results->transaction,           /* Transaction handle */
+               0,                                                      /* Timestamp buffer size */
+               NULL,                                           /* Valid after */
+               NULL,                                           /* Valid until */
+               &results->timelimit,            /* Call duration limit */
+               &callidlen,                                     /* Callid buffer size */
+               NULL,                                           /* Callid buffer */
+               sizeof(results->called),        /* Called buffer size */
+               results->called,                        /* Called buffer */
+               sizeof(results->calling),       /* Calling buffer size */
+               results->calling,                       /* Calling buffer */
+               sizeof(term),                           /* Destination buffer size */
+               term,                                           /* Destination buffer */
+               0,                                                      /* Destination device buffer size */
+               NULL,                                           /* Destination device buffer */
+               &tokenlen,                                      /* Token buffer length */
+               NULL);                                          /* Token buffer */
+       if (error != OSPC_ERR_NO_ERROR) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get first destination, error '%d'\n", error);
+       } else {
+               osp_convert_outin(term, results->dest, sizeof(results->dest));
 
-       status = osp_do_request(profile, transaction, &inbound, results);
+               /* Check destination */
+               status = osp_check_destination(results);
+       }
+       OSP_DEBUG("status: '%d'", status);
 
-       OSPPTransactionDelete(transaction);
+       OSP_DEBUG_END;
 
        return status;
 }
 
 /*
- * Build parameter string for all channels
- * param results Routing results
- * param buffer Buffer
- * param bufsize Buffer size
- * return
+ * Get next destination
+ * param results Routing info
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
  */
-static void osp_build_allparam(
-       osp_results_t *results,
-       char *buffer,
-       switch_size_t bufsize)
+static switch_status_t osp_get_next(
+       osp_results_t *results)
 {
-       char *head = buffer;
-       switch_size_t len, size = bufsize;
+       int error;
+       char term[OSP_SIZE_NORSTR];
+       unsigned int callidlen = 0, tokenlen = 0;
+       switch_status_t status = SWITCH_STATUS_FALSE;
 
-       if (results && head && size) {
-               switch_snprintf(head, size,
-                       "{%s=%s,%s=%"PRIu64",%s=%s,%s=%s,%s=%"PRId64",%s=%s,%s=%d",
-                       OSP_VAR_PROFILE, results->profile,
-                       OSP_VAR_TRANSID, results->transid,
-                       OSP_VAR_CALLING, results->calling,
-                       OSP_VAR_CALLED, results->called,
-                       OSP_VAR_START, results->start,
-                       OSP_VAR_SRCDEV, results->srcdev,
-                       OSP_VAR_DESTTOTAL, results->numdest);
-               osp_adjust_len(head, size, len);
-
-               if (!switch_strlen_zero(results->srcnid)) {
-                       switch_snprintf(head, size, ",%s=%s", OSP_VAR_SRCNID, results->srcnid);
-               }
-               osp_adjust_len(head, size, len);
+       OSP_DEBUG_START;
+
+       while ((status == SWITCH_STATUS_FALSE) && (results->count < results->total)) {
+               /* Set destination count */
+               results->count++;
+
+               OSP_DEBUG_MSG("GetNextDestination");
+
+               /* Get next destination */
+               error = OSPPTransactionGetNextDestination(
+                       results->transaction,           /* Transsaction handle */
+                       results->cause,                         /* Failure reason */
+                       0,                                                      /* Timestamp buffer size */
+                       NULL,                                           /* Valid after */
+                       NULL,                                           /* Valid until */
+                       &results->timelimit,            /* Call duration limit */
+                       &callidlen,                                     /* Callid buffer size */
+                       NULL,                                           /* Callid buffer */
+                       sizeof(results->called),        /* Called buffer size */
+                       results->called,                        /* Called buffer */
+                       sizeof(results->calling),       /* Calling buffer size */
+                       results->calling,                       /* Calling buffer */
+                       sizeof(term),                           /* Destination buffer size */
+                       term,                                           /* Destination buffer */
+                       0,                                                      /* Destination device buffer size */
+                       NULL,                                           /* Destination device buffer */
+                       &tokenlen,                                      /* Token buffer length */
+                       NULL);                                          /* Token buffer */
+               if (error != OSPC_ERR_NO_ERROR) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get destination, error '%d'\n", error);
+                       break;
+               } else {
+                       osp_convert_outin(term, results->dest, sizeof(results->dest));
 
-               switch_snprintf(head, size, "}");
-               osp_adjust_len(head, size, len);
+                       /* Check destination */
+                       status = osp_check_destination(results);
+               }
        }
+       OSP_DEBUG("status: '%d'", status);
+
+       OSP_DEBUG_END;
+
+       return status;
 }
 
 /*
- * Build parameter string for each channel
- * param count Destination count
- * param dest Destination
- * param buffer Buffer
- * param bufsize Buffer size
- * return
+ * Report usage
+ * param results Route info
+ * param usage Usage info
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
  */
-static void osp_build_eachparam(
-       int count,
-       osp_destination_t *dest,
-       char *buffer,
-       switch_size_t bufsize)
+static switch_status_t osp_report_usage(
+       osp_results_t *results,
+       osp_usage_t *usage)
 {
-       char *head = buffer;
-       switch_size_t len, size = bufsize;
+       int error;
+       unsigned int dummy = 0;
+       switch_status_t status = SWITCH_STATUS_FALSE;
 
-       if ((count > 0) && dest && head && size) {
-               switch_snprintf(buffer, bufsize,
-                       "[%s=%d,%s=%s,%s=%s",
-                       OSP_VAR_DESTCOUNT, count,
-                       OSP_VAR_DESTIP, dest->dest,
-                       OSP_FS_OUTCALLING, dest->calling);
-               osp_adjust_len(head, size, len);
+       OSP_DEBUG_START;
 
-               if (!switch_strlen_zero_buf(dest->destnid)) {
-                       switch_snprintf(head, size, ",%s=%s", OSP_VAR_DESTNID, dest->destnid);
-               }
-               osp_adjust_len(head, size, len);
+       /* Set role info */
+       OSPPTransactionSetRoleInfo(results->transaction, OSPC_RSTATE_STOP, OSPC_RFORMAT_OSP, OSPC_RVENDOR_FREESWITCH);
+
+       /* Set termination cause */
+       if (results->cause) {
+               OSPPTransactionRecordFailure(results->transaction, results->cause);
+       } else {
+               OSPPTransactionRecordFailure(results->transaction, usage->cause);
+       }
+
+       /* Set codecs */
+       if (!switch_strlen_zero(usage->srccodec)) {
+               OSPPTransactionSetCodec(results->transaction, OSPC_CODEC_SOURCE, usage->srccodec);
+       }
+       if (!switch_strlen_zero(usage->destcodec)) {
+               OSPPTransactionSetCodec(results->transaction, OSPC_CODEC_DESTINATION, usage->destcodec);
+       }
 
-               switch_snprintf(head, size, "]");
-               osp_adjust_len(head, size, len);
+       /* Set QoS statistics */
+       if (usage->rtpsrcrepoctets != OSP_DEF_STATS) {
+               OSPPTransactionSetOctets(results->transaction, OSPC_SMETRIC_RTP, OSPC_SDIR_SRCREP, usage->rtpsrcrepoctets);
+       }
+       if (usage->rtpdestrepoctets != OSP_DEF_STATS) {
+               OSPPTransactionSetOctets(results->transaction, OSPC_SMETRIC_RTP, OSPC_SDIR_DESTREP, usage->rtpdestrepoctets);
+       }
+       if (usage->rtpsrcreppackets != OSP_DEF_STATS) {
+               OSPPTransactionSetPackets(results->transaction, OSPC_SMETRIC_RTP, OSPC_SDIR_SRCREP, usage->rtpsrcreppackets);
+       }
+       if (usage->rtpdestreppackets != OSP_DEF_STATS) {
+               OSPPTransactionSetPackets(results->transaction, OSPC_SMETRIC_RTP, OSPC_SDIR_DESTREP, usage->rtpdestreppackets);
+       }
+
+       OSP_DEBUG_MSG("ReportUsage");
+
+       /* Report usage */
+       error = OSPPTransactionReportUsage(
+               results->transaction,                           /* Transaction handle */
+               usage->duration / 1000000,                      /* Duration */
+               usage->start / 1000000,                         /* Start time */
+               usage->end / 1000000,                           /* End time */
+               usage->alert / 1000000,                         /* Alert time */
+               usage->connect / 1000000,                       /* Connect time */
+               usage->alert,                                           /* If PDD exist (call ringed) */
+               usage->pdd / 1000,                                      /* Post dial delay, in ms */
+               usage->release,                                         /* Release source */
+               NULL,                                                           /* Conference ID */
+               OSP_DEF_STATS,                                          /* Loss packet sent */
+               OSP_DEF_STATS,                                          /* Loss fraction sent */
+               OSP_DEF_STATS,                                          /* Loss packet received */
+               OSP_DEF_STATS,                                          /* Loss fraction received */
+               &dummy,                                                         /* Detail log buffer size */
+               NULL);                                                          /* Detail log buffer */
+       if (error != OSPC_ERR_NO_ERROR) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to report usage, error '%d'\n", error);
+       } else {
+               status = SWITCH_STATUS_SUCCESS;
        }
+
+       /* Delete transaction handle */
+       OSPPTransactionDelete(results->transaction);
+
+       OSP_DEBUG_END;
+
+       return status;
 }
 
 /*
- * Build endpoint string
- * param dest Destination
- * param outbound Outbound settings
- * param buffer Buffer
- * param bufsize Buffer size
+ * Export OSP lookup status to channel
+ * param channel Originator channel
+ * param status OSP lookup status
+ * param outbound Outbound info
+ * param results Routing info
  * return
  */
-static void osp_build_endpoint(
-       osp_destination_t *dest,
+static void osp_export_lookup(
+       switch_channel_t *channel,
+       switch_status_t status,
        osp_outbound_t *outbound,
-       char *buffer,
-       switch_size_t bufsize)
+       osp_results_t *results)
 {
-       char *head = buffer;
-       switch_size_t len, size = bufsize;
+       char value[OSP_SIZE_ROUSTR];
 
-       if (head && size) {
-               switch (dest->protocol) {
-               case OSPC_PROTNAME_SIP:
-                       switch_snprintf(head, size, "%s/%s/%s", osp_globals.modules[OSPC_PROTNAME_SIP], osp_globals.profiles[OSPC_PROTNAME_SIP],
-                               dest->called);
-                       osp_adjust_len(head, size, len);
+       OSP_DEBUG_START;
 
-                       if (!switch_strlen_zero_buf(dest->nprn)) {
-                               switch_snprintf(head, size, ";rn=%s", dest->nprn);
-                               osp_adjust_len(head, size, len);
-                       }
-                       if (!switch_strlen_zero_buf(dest->npcic)) {
-                               switch_snprintf(head, size, ";cic=%s", dest->npcic);
-                               osp_adjust_len(head, size, len);
-                       }
-                       if (dest->npdi) {
-                               switch_snprintf(head, size, ";npdi");
-                               osp_adjust_len(head, size, len);
-                       }
+       /* Profile name */
+       switch_channel_set_variable_var_check(channel, OSP_VAR_PROFILE, results->profile, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_PROFILE, results->profile);
 
-                       if (!switch_strlen_zero(outbound->dniduserparam) && !switch_strlen_zero_buf(dest->destnid)) {
-                               switch_snprintf(head, size, ";%s=%s", outbound->dniduserparam, dest->destnid);
-                               osp_adjust_len(head, size, len);
-                       }
+       /* Transaction handle */
+       switch_snprintf(value, sizeof(value), "%d", results->transaction);
+       switch_channel_set_variable_var_check(channel, OSP_VAR_TRANSACTION, value, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_TRANSACTION, value);
 
-                       switch_snprintf(head, size, "@%s", dest->dest);
-                       osp_adjust_len(head, size, len);
+       /* Transaction ID */
+       switch_snprintf(value, sizeof(value), "%"PRIu64"", results->transid);
+       switch_channel_set_variable_var_check(channel, OSP_VAR_TRANSID, value, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_TRANSID, value);
 
-                       if (!switch_strlen_zero(outbound->dniduriparam) && !switch_strlen_zero_buf(dest->destnid)) {
-                               switch_snprintf(head, size, ";%s=%s", outbound->dniduriparam, dest->destnid);
-                               osp_adjust_len(head, size, len);
-                       }
+       /* OSP lookup status */
+       switch_snprintf(value, sizeof(value), "%d", status);
+       switch_channel_set_variable_var_check(channel, OSP_VAR_LOOKUPSTATUS, value, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_LOOKUPSTATUS, value);
 
-                       if (outbound->userphone) {
-                               switch_snprintf(head, size, ";user=phone");
-                               osp_adjust_len(head, size, len);
-                       }
+       /* Destination total */
+       switch_snprintf(value, sizeof(value), "%d", results->total);
+       switch_channel_set_variable_var_check(channel, OSP_VAR_ROUTETOTAL, value, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_ROUTETOTAL, value);
 
-                       if (!switch_strlen_zero(outbound->outproxy)) {
-                               switch_snprintf(head, size, ";fs_path=sip:%s", outbound->outproxy);
-                               osp_adjust_len(head, size, len);
-                       }
-                       break;
-               case OSPC_PROTNAME_Q931:
-                       switch_snprintf(head, size, "%s/%s/%s@%s", osp_globals.modules[OSPC_PROTNAME_Q931], osp_globals.profiles[OSPC_PROTNAME_Q931],
-                               dest->called, dest->dest);
-                       osp_adjust_len(head, size, len);
-                       break;
-               case OSPC_PROTNAME_IAX:
-                       switch_snprintf(head, size, "%s/%s/%s/%s", osp_globals.modules[OSPC_PROTNAME_Q931], osp_globals.profiles[OSPC_PROTNAME_Q931],
-                               dest->dest, dest->called);
-                       osp_adjust_len(head, size, len);
-                       break;
-               case OSPC_PROTNAME_SKYPE:
-                       switch_snprintf(head, size, "%s/%s/%s", osp_globals.modules[OSPC_PROTNAME_Q931], osp_globals.profiles[OSPC_PROTNAME_Q931],
-                               dest->called);
-                       osp_adjust_len(head, size, len);
-                       break;
-               default:
-                       buffer[0] = '\0';
-                       break;
-               }
+       /* Destination count */
+       switch_snprintf(value, sizeof(value), "%d", results->count);
+       switch_channel_set_variable_var_check(channel, OSP_VAR_ROUTECOUNT, value, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_ROUTECOUNT, value);
+
+       /* Dial string */
+       if (status == SWITCH_STATUS_SUCCESS) {
+               osp_create_route(outbound, results, value, sizeof(value));
+               switch_channel_set_variable_var_check(channel, OSP_VAR_AUTOROUTE, value, SWITCH_FALSE);
+       } else {
+               value[0] = '\0';
+               switch_channel_set_variable(channel, OSP_VAR_AUTOROUTE, NULL);
        }
+       OSP_DEBUG("%s: '%s'", OSP_VAR_AUTOROUTE, value);
+
+       /* Termiantion cause */
+       if (results->cause) {
+               switch_snprintf(value, sizeof(value), "%d", results->cause);
+               switch_channel_set_variable_var_check(channel, OSP_VAR_TCCODE, value, SWITCH_FALSE);
+       } else {
+               value[0] = '\0';
+               switch_channel_set_variable(channel, OSP_VAR_TCCODE, NULL);
+       }
+       OSP_DEBUG("%s: '%s'", OSP_VAR_TCCODE, value);
+
+       OSP_DEBUG_END;
 }
 
 /*
- * Create route string
+ * Export OSP next status to channel
  * param channel Originator channel
+ * param status OSP next status
+ * param outbound Outbound info
  * param results Routing info
  * return
  */
-static void osp_create_route(
+static void osp_export_next(
        switch_channel_t *channel,
+       switch_status_t status,
+       osp_outbound_t *outbound,
        osp_results_t *results)
 {
-       osp_destination_t *dest;
-       char name[OSP_SIZE_NORSTR];
-       char value[OSP_SIZE_ROUSTR];
-       char allparam[OSP_SIZE_NORSTR];
-       char eachparam[OSP_SIZE_NORSTR];
-       char endpoint[OSP_SIZE_NORSTR];
-       osp_outbound_t outbound;
-       char tmp[OSP_SIZE_ROUSTR];
-       char buffer[OSP_SIZE_ROUSTR];
-       int i, len, count, size = sizeof(buffer);
-       char *head = buffer;
-       switch_event_header_t *hi;
-       char *var;
-
-       osp_get_outbound(channel, &outbound);
-
-       /* Cleanup OSP varibales in originator */
-       if ((hi = switch_channel_variable_first(channel))) {
-               for (; hi; hi = hi->next) {
-                       var = hi->name;
-                       if (var && !strncmp(var, "osp_", 4)) {
-                               switch_channel_set_variable(channel, var, NULL);
-                       }
-               }
-               switch_channel_variable_last(channel);
-       }
-
-       switch_snprintf(value, sizeof(value), "%d", results->status);
-       switch_channel_set_variable_var_check(channel, OSP_VAR_AUTHSTATUS, value, SWITCH_FALSE);
+       char value[OSP_SIZE_NORSTR];
 
-       osp_build_allparam(results, head, size);
-       switch_copy_string(allparam, head, sizeof(allparam));
-       osp_adjust_len(head, size, len);
+       OSP_DEBUG_START;
 
-       for (count = 0, i = 0; i < results->numdest; i++) {
-               dest = &results->dests[i];
-               if (dest->supported) {
-                       count++;
-                       osp_build_eachparam(i + 1, dest, eachparam, sizeof(eachparam));
-                       osp_build_endpoint(dest, &outbound, endpoint, sizeof(endpoint));
+       /* OSP next status */
+       switch_snprintf(value, sizeof(value), "%d", status);
+       switch_channel_set_variable_var_check(channel, OSP_VAR_NEXTSTATUS, value, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_NEXTSTATUS, value);
 
-                       switch_snprintf(name, sizeof(name), "%s%d", OSP_VAR_ROUTEPRE, count);
-                       switch_snprintf(value, sizeof(value), "%s%s%s", allparam, eachparam, endpoint);
-                       switch_channel_set_variable_var_check(channel, name, value, SWITCH_FALSE);
+       /* Destination count */
+       switch_snprintf(value, sizeof(value), "%d", results->count);
+       switch_channel_set_variable_var_check(channel, OSP_VAR_ROUTECOUNT, value, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_ROUTECOUNT, value);
 
-                       switch_snprintf(tmp, sizeof(tmp), "%s%s", eachparam, endpoint);
-                       switch_snprintf(head, size, "%s|", tmp);
-                       osp_adjust_len(head, size, len);
-               }
+       /* Dial string */
+       if (status == SWITCH_STATUS_SUCCESS) {
+               osp_create_route(outbound, results, value, sizeof(value));
+               switch_channel_set_variable_var_check(channel, OSP_VAR_AUTOROUTE, value, SWITCH_FALSE);
+       } else {
+               value[0] = '\0';
+               switch_channel_set_variable(channel, OSP_VAR_AUTOROUTE, NULL);
        }
+       OSP_DEBUG("%s: '%s'", OSP_VAR_AUTOROUTE, value);
 
-       switch_snprintf(value, sizeof(value), "%d", count);
-       switch_channel_set_variable_var_check(channel, OSP_VAR_ROUTECOUNT, value, SWITCH_FALSE);
-
-       if (count) {
-               *(buffer + strlen(buffer) - 1) = '\0';
+       /* Termiantion cause */
+       if (results->cause) {
+               switch_snprintf(value, sizeof(value), "%d", results->cause);
+               switch_channel_set_variable_var_check(channel, OSP_VAR_TCCODE, value, SWITCH_FALSE);
        } else {
-               buffer[0] = '\0';
+               value[0] = '\0';
+               switch_channel_set_variable(channel, OSP_VAR_TCCODE, NULL);
        }
-       switch_channel_set_variable_var_check(channel, OSP_VAR_AUTOROUTE, buffer, SWITCH_FALSE);
+       OSP_DEBUG("%s: '%s'", OSP_VAR_TCCODE, value);
+
+       OSP_DEBUG_END;
 }
 
 /*
- * Export AuthReq status to channel
+ * Request auth and get first OSP route
  * param channel Originator channel
  * param results Routing info
  * return
  */
-static void osp_export_failure(
+static void osp_do_lookup(
        switch_channel_t *channel,
        osp_results_t *results)
 {
-       char value[OSP_SIZE_NORSTR];
-       switch_event_header_t *hi;
-       char *var;
-
-       /* Cleanup OSP varibales in originator */
-       if ((hi = switch_channel_variable_first(channel))) {
-               for (; hi; hi = hi->next) {
-                       var = hi->name;
-                       if (var && !strncmp(var, "osp_", 4)) {
-                               switch_channel_set_variable(channel, var, NULL);
+       int error;
+       osp_profile_t *profile;
+       osp_inbound_t inbound;
+       osp_outbound_t outbound;
+       switch_status_t status = SWITCH_STATUS_FALSE;
+
+       OSP_DEBUG_START;
+
+       if (osp_find_profile(results->profile, &profile) == SWITCH_STATUS_FALSE) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to find profile '%s'\n", results->profile);
+       } else if (profile->provider == OSP_INVALID_HANDLE) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Disabled profile '%s'\n", results->profile);
+       } else if ((error = OSPPTransactionNew(profile->provider, &results->transaction)) != OSPC_ERR_NO_ERROR) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create transaction handle, error '%d'\n", error);
+       } else {
+               /* Get inbound info */
+               osp_get_inbound(channel, &inbound);
+
+               /* Log AuthReq parameters */
+               osp_log_authreq(profile, &inbound);
+
+               /* Do AuthReq */
+               if (osp_request_auth(profile, &inbound, results) == SWITCH_STATUS_SUCCESS) {
+                       /* Get route */
+                       if ((osp_get_first(results) == SWITCH_STATUS_SUCCESS) || (osp_get_next(results) == SWITCH_STATUS_SUCCESS)) {
+                               /* Get outbound info */
+                               osp_get_outbound(channel, &outbound);
+                               status = SWITCH_STATUS_SUCCESS;
                        }
                }
-               switch_channel_variable_last(channel);
        }
 
-       switch_snprintf(value, sizeof(value), "%d", results->status);
-       switch_channel_set_variable_var_check(channel, OSP_VAR_AUTHSTATUS, value, SWITCH_FALSE);
+       /* Export OSP lookup info */
+       osp_export_lookup(channel, status, &outbound, results);
 
-       switch_snprintf(value, sizeof(value), "%d", results->numdest);
-       switch_channel_set_variable_var_check(channel, OSP_VAR_ROUTECOUNT, value, SWITCH_FALSE);
+       OSP_DEBUG_END;
 }
 
 /*
- * Macro expands to:
- * static void osp_app_function(switch_core_session_t *session, const char *data)
+ * Get next OSP route
+ * param channel Originator channel
+ * param results Routing info
+ * return
  */
-SWITCH_STANDARD_APP(osp_app_function)
+static void osp_do_next(
+       switch_channel_t *channel,
+       osp_results_t *results)
 {
-       int argc = 0;
-       char *argv[2] = { 0 };
-       char *params = NULL;
-       char *profile = NULL;
-       switch_channel_t *channel;
-       osp_results_t results;
-       switch_status_t retval;
-
-       if (osp_globals.shutdown) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "OSP application inavailable\n");
-               return;
-       }
+       osp_outbound_t outbound;
+       switch_status_t status = SWITCH_STATUS_FALSE;
 
-       /* Make sure there is a valid channel when starting the OSP application */
-       if (!(channel = switch_core_session_get_channel(session))) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to find origiantor channel\n");
-               return;
-       }
+       OSP_DEBUG_START;
 
-       if (!(params = switch_core_session_strdup(session, data))) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to alloc parameters\n");
-               return;
+       if (osp_get_transaction(channel, SWITCH_FALSE, results) == SWITCH_STATUS_SUCCESS) {
+               /* Get next OSP route */
+               if ((status = osp_get_next(results)) == SWITCH_STATUS_SUCCESS) {
+                       /* Get outbound info */
+                       osp_get_outbound(channel, &outbound);
+               }
        }
 
-       if ((argc = switch_separate_string(params, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
-               profile = argv[0];
-       } else {
-               profile = OSP_DEF_PROFILE;
-       }
+       /* Export OSP next info */
+       osp_export_next(channel, status, &outbound, results);
 
-       retval = osp_request_routing(channel, profile, &results);
-       if (retval == SWITCH_STATUS_SUCCESS) {
-               osp_create_route(channel, &results);
-       } else {
-               osp_export_failure(channel, &results);
-       }
+       OSP_DEBUG_END;
 }
 
 /*
- * Add application
- * param session
+ * Report OSP usage
  * param channel Originator channel
- * param extenstion
- * param results OSP routing info
- * return
+ * param originator Originate profile
+ * param terminator Terminate profile
+ * param results Routing info
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
  */
-static void osp_add_application(
-       switch_core_session_t *session,
+static switch_status_t osp_do_report(
        switch_channel_t *channel,
-       switch_caller_extension_t **extension,
+       switch_caller_profile_t *originator,
+       switch_caller_profile_t *terminator,
        osp_results_t *results)
 {
-       osp_destination_t *dest;
-       char allparam[OSP_SIZE_NORSTR];
-       char eachparam[OSP_SIZE_NORSTR];
-       char endpoint[OSP_SIZE_NORSTR];
-       osp_outbound_t outbound;
-       char name[OSP_SIZE_NORSTR];
-       char value[OSP_SIZE_ROUSTR];
-       int i, count;
-       switch_event_header_t *hi;
-       char *var;
+       osp_usage_t usage;
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
 
-       if ((*extension = switch_caller_extension_new(session, results->called, results->called)) == 0) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to create extension\n");
-               return;
-       }
+       OSP_DEBUG_START;
 
-       osp_get_outbound(channel, &outbound);
+       if (osp_get_transaction(channel, SWITCH_TRUE, results) == SWITCH_STATUS_SUCCESS) {
+               /* Do not report usage for failed AuthReq */
+               if (results->total) {
+                       /* Get usage info */
+                       osp_get_usage(channel, originator, terminator, results, &usage);
 
-       switch_channel_set_variable(channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE, "true");
+                       /* Log usage info */
+                       osp_log_usageind(results, &usage);
 
-       /* Cleanup OSP varibales in originator */
-       if ((hi = switch_channel_variable_first(channel))) {
-               for (; hi; hi = hi->next) {
-                       var = hi->name;
-                       if (var && !strncmp(var, "osp_", 4)) {
-                               switch_channel_set_variable(channel, var, NULL);
-                       }
+                       /* Report OSP usage */
+                       status = osp_report_usage(results, &usage);
+               } else {
+                       OSP_DEBUG_MSG("Do not report usage");
                }
-               switch_channel_variable_last(channel);
        }
 
-       switch_snprintf(value, sizeof(value), "%d", results->status);
-       switch_channel_set_variable_var_check(channel, OSP_VAR_AUTHSTATUS, value, SWITCH_FALSE);
-
-       osp_build_allparam(results, allparam, sizeof(allparam));
+       OSP_DEBUG_END;
 
-       for (count = 0, i = 0; i < results->numdest; i++) {
-               dest = &results->dests[i];
-               if (dest->supported) {
-                       count++;
-                       osp_build_eachparam(i + 1, dest, eachparam, sizeof(eachparam));
-                       osp_build_endpoint(dest, &outbound, endpoint, sizeof(endpoint));
-
-                       switch_snprintf(name, sizeof(name), "%s%d", OSP_VAR_ROUTEPRE, count);
-                       switch_snprintf(value, sizeof(value), "%s%s%s", allparam, eachparam, endpoint);
-                       switch_channel_set_variable_var_check(channel, name, value, SWITCH_FALSE);
-
-                       switch_caller_extension_add_application(session, *extension, "bridge", value);
-               }
-       }
-
-       switch_snprintf(value, sizeof(value), "%d", count);
-       switch_channel_set_variable_var_check(channel, OSP_VAR_ROUTECOUNT, value, SWITCH_FALSE);
+       return status;
 }
 
 /*
+ * OSP module CLI command
  * Macro expands to:
- * switch_caller_extension_t * osp_dialplan_function(switch_core_session_t *session, void *arg, switch_caller_profile_t *caller_profile)
+ * static switch_status_t osp_cli_function(_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream)
  */
-SWITCH_STANDARD_DIALPLAN(osp_dialplan_function)
+SWITCH_STANDARD_API(osp_cli_function)
 {
-       int argc = 0;
+       int i, argc = 0;
        char *argv[2] = { 0 };
-       char *profile = NULL;
-       switch_caller_extension_t *extension = NULL;
-       switch_channel_t *channel = switch_core_session_get_channel(session);
-       osp_results_t results;
-       switch_status_t retval;
-
-       if (osp_globals.shutdown) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "OSP dialplan inavailable\n");
-               return extension;
-       }
-
-       if ((argc = switch_separate_string((char *)arg, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
-               profile = argv[0];
-       } else {
-               profile = OSP_DEF_PROFILE;
-       }
-
-       retval = osp_request_routing(channel, profile, &results);
-       if (retval == SWITCH_STATUS_SUCCESS) {
-               osp_add_application(session, channel, &extension, &results);
-       } else {
-               osp_export_failure(channel, &results);
-       }
-
-       return extension;
-}
+       char *params = NULL;
+       char *param = NULL;
+       osp_profile_t *profile;
+       char *loglevel;
 
-/*
- * Retrieve cookie
- * param channel Destination channel
- * param cookie Cookie
- * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
- */
-static switch_status_t osp_get_cookie(
-       switch_channel_t *channel,
-       osp_cookie_t *cookie)
-{
-       const char *strvar;
+       OSP_DEBUG_START;
 
-       if (!(cookie->profile = switch_channel_get_variable(channel, OSP_VAR_PROFILE))) {
+       if (session) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function cannot be called from the dialplan.\n");
+               OSP_DEBUG_END;
                return SWITCH_STATUS_FALSE;
        }
 
-       if (!(strvar = switch_channel_get_variable(channel, OSP_VAR_TRANSID)) || (sscanf(strvar, "%"PRIu64"", &cookie->transid) != 1)) {
-               cookie->transid = 0;
-       }
-
-       cookie->calling = switch_channel_get_variable(channel, OSP_VAR_CALLING);
-       cookie->called = switch_channel_get_variable(channel, OSP_VAR_CALLED);
-
-       if (!(strvar = switch_channel_get_variable(channel, OSP_VAR_START)) || (sscanf(strvar, "%"PRId64"", &cookie->start) != 1)) {
-               cookie->start = 0;
+       if (switch_strlen_zero(cmd)) {
+               stream->write_function(stream, "Usage: osp status\n");
+               OSP_DEBUG_END;
+               return SWITCH_STATUS_SUCCESS;
        }
 
-       cookie->srcdev = switch_channel_get_variable(channel, OSP_VAR_SRCDEV);
-
-       if (!(strvar = switch_channel_get_variable(channel, OSP_VAR_DESTTOTAL)) || (sscanf(strvar, "%d", &cookie->desttotal) != 1)) {
-               cookie->desttotal = 0;
+       if (!(params = switch_safe_strdup(cmd))) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to duplicate parameters\n");
+               OSP_DEBUG_END;
+               return SWITCH_STATUS_MEMERR;
        }
 
-       if (!(strvar = switch_channel_get_variable(channel, OSP_VAR_DESTCOUNT)) || (sscanf(strvar, "%d", &cookie->destcount) != 1)) {
-               cookie->destcount = 0;
+       if ((argc = switch_separate_string(params, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+               param = argv[0];
+               if (!strcasecmp(param, "status")) {
+                       stream->write_function(stream, "=============== OSP Module Settings & Status ===============\n");
+                       stream->write_function(stream, "                debug-info: %s\n", osp_global.debug ? "enabled" : "disabled");
+                       switch (osp_global.loglevel) {
+                       case SWITCH_LOG_CONSOLE:
+                               loglevel = "console";
+                               break;
+                       case SWITCH_LOG_ALERT:
+                               loglevel = "alert";
+                               break;
+                       case SWITCH_LOG_CRIT:
+                               loglevel = "crit";
+                               break;
+                       case SWITCH_LOG_ERROR:
+                               loglevel = "error";
+                               break;
+                       case SWITCH_LOG_WARNING:
+                               loglevel = "warning";
+                               break;
+                       case SWITCH_LOG_NOTICE:
+                               loglevel = "notice";
+                               break;
+                       case SWITCH_LOG_INFO:
+                               loglevel = "info";
+                               break;
+                       case SWITCH_LOG_DEBUG:
+                       default:
+                               loglevel = "debug";
+                               break;
+                       }
+                       stream->write_function(stream, "                 log-level: %s\n", loglevel);
+                       stream->write_function(stream, "           crypto-hardware: %s\n", osp_global.hardware ? "enabled" : "disabled");
+                       if (switch_strlen_zero(osp_global.endpoint[OSPC_PROTNAME_SIP].module) || switch_strlen_zero(osp_global.endpoint[OSPC_PROTNAME_SIP].profile)) {
+                               stream->write_function(stream, "                       sip: unsupported\n");
+                       } else {
+                               stream->write_function(stream, "                       sip: %s/%s\n",
+                                       osp_global.endpoint[OSPC_PROTNAME_SIP].module, osp_global.endpoint[OSPC_PROTNAME_SIP].profile);
+                       }
+                       if (switch_strlen_zero(osp_global.endpoint[OSPC_PROTNAME_Q931].module) || switch_strlen_zero(osp_global.endpoint[OSPC_PROTNAME_Q931].profile)) {
+                               stream->write_function(stream, "                      h323: unsupported\n");
+                       } else {
+                               stream->write_function(stream, "                      h323: %s/%s\n",
+                                       osp_global.endpoint[OSPC_PROTNAME_Q931].module, osp_global.endpoint[OSPC_PROTNAME_Q931].profile);
+                       }
+                       if (switch_strlen_zero(osp_global.endpoint[OSPC_PROTNAME_IAX].module) || switch_strlen_zero(osp_global.endpoint[OSPC_PROTNAME_IAX].profile)) {
+                               stream->write_function(stream, "                       iax: unsupported\n");
+                       } else {
+                               stream->write_function(stream, "                       iax: %s/%s\n",
+                                       osp_global.endpoint[OSPC_PROTNAME_IAX].module, osp_global.endpoint[OSPC_PROTNAME_IAX].profile);
+                       }
+                       if (switch_strlen_zero(osp_global.endpoint[OSPC_PROTNAME_SKYPE].module) || switch_strlen_zero(osp_global.endpoint[OSPC_PROTNAME_SKYPE].profile)) {
+                               stream->write_function(stream, "                     skype: unsupported\n");
+                       } else {
+                               stream->write_function(stream, "                     skype: %s/%s\n",
+                                       osp_global.endpoint[OSPC_PROTNAME_SKYPE].module, osp_global.endpoint[OSPC_PROTNAME_SKYPE].profile);
+                       }
+                       stream->write_function(stream, "          default-protocol: %s\n", osp_get_protocolname(osp_global.protocol));
+                       stream->write_function(stream, "============== OSP Profile Settings & Status ==============\n");
+                       for (profile = osp_profiles; profile; profile = profile->next) {
+                               stream->write_function(stream, "Profile: %s\n", profile->name);
+                               for (i = 0; i < profile->spnumber; i++) {
+                                       stream->write_function(stream, "         service-point-url: %s\n", profile->spurl[i]);
+                               }
+                               stream->write_function(stream, "                 device-ip: %s\n", profile->deviceip);
+                               stream->write_function(stream, "              ssl-lifetime: %d\n", profile->lifetime);
+                               stream->write_function(stream, "      http-max-connections: %d\n", profile->maxconnect);
+                               stream->write_function(stream, "          http-persistence: %d\n", profile->persistence);
+                               stream->write_function(stream, "          http-retry-dalay: %d\n", profile->retrydelay);
+                               stream->write_function(stream, "          http-retry-limit: %d\n", profile->retrylimit);
+                               stream->write_function(stream, "              http-timeout: %d\n", profile->timeout);
+                               switch (profile->workmode) {
+                               case OSP_MODE_DIRECT:
+                                       stream->write_function(stream, "                 work-mode: direct\n");
+                                       break;
+                               case OSP_MODE_INDIRECT:
+                               default:
+                                       stream->write_function(stream, "                 work-mode: indirect\n");
+                                       break;
+                               }
+                               switch (profile->srvtype) {
+                               case OSP_SRV_NPQUERY:
+                                       stream->write_function(stream, "              service-type: npquery\n");
+                                       break;
+                               case OSP_SRV_VOICE:
+                               default:
+                                       stream->write_function(stream, "              service-type: voice\n");
+                                       break;
+                               }
+                               stream->write_function(stream, "          max-destinations: %d\n", profile->maxdest);
+                               stream->write_function(stream, "                    status: %s\n", profile->provider != OSP_INVALID_HANDLE ? "enabled" : "disabled");
+                       }
+               } else {
+                       stream->write_function(stream, "Invalid Syntax!\n");
+               }
+       } else {
+               stream->write_function(stream, "Invalid Input!\n");
        }
 
-       cookie->dest = switch_channel_get_variable(channel, OSP_VAR_DESTIP);
-
-       cookie->srcnid = switch_channel_get_variable(channel, OSP_VAR_SRCNID);
+       switch_safe_free(params);
 
-       cookie->destnid = switch_channel_get_variable(channel, OSP_VAR_DESTNID);
+       OSP_DEBUG_END;
 
        return SWITCH_STATUS_SUCCESS;
 }
 
 /*
- * Retrieve usage info
- * param channel Destination channel
- * param originator Originator channel
- * param cookie Cookie
- * param usage Usage info
- * return
+ * Macro expands to:
+ * static void osp_lookup_function(switch_core_session_t *session, const char *data)
  */
-static void osp_get_usage(
-       switch_channel_t *channel,
-       switch_caller_profile_t *originator,
-       osp_cookie_t *cookie,
-       osp_usage_t *usage)
+SWITCH_STANDARD_APP(osp_lookup_function)
 {
-       const char *strvar;
-       switch_caller_profile_t *terminator;
-       switch_channel_timetable_t *times;
-
-       memset(usage, 0, sizeof(*usage));
-
-       usage->callid = switch_channel_get_variable(channel, OSP_FS_OUTCALLID);
-       if (switch_strlen_zero(usage->callid)) {
-               usage->callid = OSP_DEF_CALLID;
-       }
-
-       /* Originator had been checked by osp_on_reporting */
-       if (originator) {
-               usage->srcdev = originator->network_addr;
-               usage->inprotocol = osp_get_moduleprotocol(originator->source);
-       }
-
-       terminator = switch_channel_get_caller_profile(channel);
-       usage->outprotocol = osp_get_moduleprotocol(terminator->source);
-       if (usage->outprotocol == OSPC_PROTNAME_SIP) {
-               strvar = switch_channel_get_variable(channel, OSP_FS_SIPRELEASE);
-               if (!strvar || !strcasecmp(strvar, "recv_bye")) {
-                       usage->release = 1;
-               }
-       }
-       usage->cause = switch_channel_get_cause_q850(channel);
-       times = switch_channel_get_timetable(channel);
-       usage->alert = times->progress;
-       usage->connect = times->answered;
-       usage->end = times->hungup;
-       if (times->answered) {
-               usage->duration = times->hungup - times->answered;
-               usage->pdd = times->answered - cookie->start;
-       }
+       switch_channel_t *channel;
+       int argc = 0;
+       char *argv[2] = { 0 };
+       char *params = NULL;
+       osp_results_t results;
 
-       usage->srccodec = switch_channel_get_variable(channel, OSP_FS_SRCCODEC);
-       usage->destcodec = switch_channel_get_variable(channel, OSP_FS_DESTCODEC);
-       if (!(strvar = switch_channel_get_variable(channel, OSP_FS_RTPSRCREPOCTS)) ||
-               (sscanf(strvar, "%d", &usage->rtpsrcrepoctets) != 1))
-       {
-               usage->rtpsrcrepoctets = OSP_DEF_STATS;
-       }
-       if (!(strvar = switch_channel_get_variable(channel, OSP_FS_RTPDESTREPOCTS)) ||
-               (sscanf(strvar, "%d", &usage->rtpdestrepoctets) != 1))
-       {
-               usage->rtpdestrepoctets = OSP_DEF_STATS;
-       }
-       if (!(strvar = switch_channel_get_variable(channel, OSP_FS_RTPSRCREPPKTS)) ||
-               (sscanf(strvar, "%d", &usage->rtpsrcreppackets) != 1))
-       {
-               usage->rtpsrcreppackets = OSP_DEF_STATS;
-       }
-       if (!(strvar = switch_channel_get_variable(channel, OSP_FS_RTPDESTREPPKTS)) ||
-               (sscanf(strvar, "%d", &usage->rtpdestreppackets) != 1))
-       {
-               usage->rtpdestreppackets = OSP_DEF_STATS;
-       }
-}
+       OSP_DEBUG_START;
 
-/*
- * Report OSP usage thread function
- * param threadarg Thread argments
- * return
- */
-static OSPTTHREADRETURN osp_report_thread(
-       void *threadarg)
-{
-       int i, error;
-       osp_threadarg_t *info;
-
-       info = (osp_threadarg_t *)threadarg;
-
-       OSPPTransactionRecordFailure(info->transaction, info->cause);
-
-       for (i = 0; i < 3; i++) {
-               error = OSPPTransactionReportUsage(
-                       info->transaction,      /* Transaction handle */
-                       info->duration,         /* Call duration */
-                       info->start,            /* Call start time */
-                       info->end,                      /* Call end time */
-                       info->alert,            /* Call alert time */
-                       info->connect,          /* Call connect time */
-                       info->pdd != 0,         /* Post dial delay present */
-                       info->pdd,                      /* Post dial delay */
-                       info->release,          /* Release source */
-                       NULL,                           /* Conference ID */
-                       -1,                                     /* Packets not received by peer */
-                       -1,                                     /* Fraction of packets not received by peer */
-                       -1,                                     /* Packets not received that were expected */
-                       -1,                                     /* Fraction of packets expected but not received */
-                       NULL,                           /* Log buffer size */
-                       NULL);                          /* Log buffer */
-               if (error != OSPC_ERR_NO_ERROR) {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
-                               "Failed to report usage for '%"PRIu64"' attempt '%d'\n",
-                               info->transid,
-                               i + 1);
+       if (osp_global.shutdown) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "OSP application inavailable\n");
+       } else if (!(channel = switch_core_session_get_channel(session))) {
+               /* Make sure there is a valid channel when starting the OSP application */
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to find origiantor channel\n");
+       } else if (!(params = switch_core_session_strdup(session, data))) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to alloc parameters\n");
        } else {
-                       break;
+               memset(&results, 0, sizeof(osp_results_t));
+               if ((argc = switch_separate_string(params, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+                       results.profile = argv[0];
+               } else {
+                       results.profile = OSP_DEF_PROFILE;
                }
-       }
-
-       OSPPTransactionDelete(info->transaction);
+               results.transaction = OSP_INVALID_HANDLE;
+               results.protocol = OSPC_PROTNAME_UNKNOWN;
 
-       switch_safe_free(info);
+               /* Do OSP lookup */
+               osp_do_lookup(channel, &results);
+       }
 
-       OSPTTHREADRETURN_NULL();
+       OSP_DEBUG_END;
 }
 
 /*
- * Report usage
- * param cookie Cookie
- * param usage Usage
- * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
+ * Macro expands to:
+ * static void osp_next_function(switch_core_session_t *session, const char *data)
  */
-static switch_status_t osp_report_usage(
-       osp_cookie_t *cookie,
-       osp_usage_t *usage)
+SWITCH_STANDARD_APP(osp_next_function)
 {
-       osp_profile_t *profile;
-       char source[OSP_SIZE_NORSTR];
-       char destination[OSP_SIZE_NORSTR];
-       char srcdev[OSP_SIZE_NORSTR];
-       OSPTTRANHANDLE transaction;
-       osp_threadarg_t *info;
-       OSPTTHREADID threadid;
-       OSPTTHRATTR threadattr;
-       int error;
-       switch_status_t status = SWITCH_STATUS_FALSE;
-
-       if (osp_find_profile(cookie->profile, &profile) != SWITCH_STATUS_SUCCESS) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to find profile '%s'\n", cookie->profile);
-               return status;
-       }
+       switch_channel_t *channel;
+       osp_results_t results;
 
-       if ((error = OSPPTransactionNew(profile->provider, &transaction)) != OSPC_ERR_NO_ERROR) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create transaction handle, error '%d'\n", error);
-               return status;
-       }
+       OSP_DEBUG_START;
 
-       if (profile->workmode == OSP_MODE_INDIRECT) {
-               osp_convert_inout(usage->srcdev, source, sizeof(source));
+       if (osp_global.shutdown) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "OSP application inavailable\n");
+       } else if (!(channel = switch_core_session_get_channel(session))) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to find origiantor channel\n");
        } else {
-               osp_convert_inout(profile->device, source, sizeof(source));
-       }
-
-       osp_convert_inout(cookie->dest, destination, sizeof(destination));
-
-       osp_convert_inout(cookie->srcdev, srcdev, sizeof(srcdev));
+               memset(&results, 0, sizeof(osp_results_t));
+               results.transaction = OSP_INVALID_HANDLE;
+               results.protocol = OSPC_PROTNAME_UNKNOWN;
 
-       error = OSPPTransactionBuildUsageFromScratch(
-               transaction,                    /* Transaction handle */
-               cookie->transid,                /* Transaction ID */
-               OSPC_ROLE_SOURCE,               /* CDR type, source */
-               source,                                 /* Source */
-               destination,                    /* Destination */
-               srcdev,                                 /* Source device */
-               OSP_DEF_STRING,                 /* Destination device */
-               cookie->calling,                /* Calling */
-               OSPC_NFORMAT_E164,              /* Calling format */
-               cookie->called,                 /* Called */
-               OSPC_NFORMAT_E164,              /* Called format */
-               strlen(usage->callid),  /* Size of Call-ID */
-               usage->callid,                  /* Call-ID */
-               0,                                              /* Failure reason */
-               NULL,                                   /* Log buffer size */
-               NULL);                                  /* Log buffer */
-       if (error != OSPC_ERR_NO_ERROR) {
-               OSPPTransactionDelete(transaction);
-               return status;
+               /* Do OSP next */
+               osp_do_next(channel, &results);
        }
 
-       status = SWITCH_STATUS_SUCCESS;
-
-       OSPPTransactionSetDestinationCount(transaction, cookie->destcount);
-
-       OSPPTransactionSetProtocol(transaction, OSPC_PROTTYPE_SOURCE, usage->inprotocol);
-       OSPPTransactionSetProtocol(transaction, OSPC_PROTTYPE_DESTINATION, usage->outprotocol);
-
-       if (!switch_strlen_zero(cookie->srcnid)) {
-               OSPPTransactionSetSrcNetworkId(transaction, cookie->srcnid);
-       }
-
-       if (!switch_strlen_zero(cookie->destnid)) {
-               OSPPTransactionSetDestNetworkId(transaction, cookie->destnid);
-       }
-
-       if (!switch_strlen_zero(usage->srccodec)) {
-               OSPPTransactionSetCodec(transaction, OSPC_CODEC_SOURCE, usage->srccodec);
-       }
-       if (!switch_strlen_zero(usage->destcodec)) {
-               OSPPTransactionSetCodec(transaction, OSPC_CODEC_DESTINATION, usage->destcodec);
-       }
-
-       if (usage->rtpsrcrepoctets != OSP_DEF_STATS) {
-               OSPPTransactionSetOctets(transaction, OSPC_SMETRIC_RTP, OSPC_SDIR_SRCREP, usage->rtpsrcrepoctets);
-       }
-       if (usage->rtpdestrepoctets != OSP_DEF_STATS) {
-               OSPPTransactionSetOctets(transaction, OSPC_SMETRIC_RTP, OSPC_SDIR_DESTREP, usage->rtpdestrepoctets);
-       }
-       if (usage->rtpsrcreppackets != OSP_DEF_STATS) {
-               OSPPTransactionSetPackets(transaction, OSPC_SMETRIC_RTP, OSPC_SDIR_SRCREP, usage->rtpsrcreppackets);
-       }
-       if (usage->rtpdestreppackets != OSP_DEF_STATS) {
-               OSPPTransactionSetPackets(transaction, OSPC_SMETRIC_RTP, OSPC_SDIR_DESTREP, usage->rtpdestreppackets);
-       }
-
-/* TODO: The logic to identify the last call attempt needs improvement.
-       if ((cookie->destcount == cookie->desttotal) || (usage->cause == SWITCH_CAUSE_NORMAL_CLEARING)) {
-               OSPPTransactionSetRoleInfo(transaction, OSPC_RSTATE_STOP, OSPC_RFORMAT_OSP, OSPC_RVENDOR_FREESWITCH);
-       } else {
-               OSPPTransactionSetRoleInfo(transaction, OSPC_RSTATE_INTERIM, OSPC_RFORMAT_OSP, OSPC_RVENDOR_FREESWITCH);
-       }
-*/
-       OSPPTransactionSetRoleInfo(transaction, OSPC_RSTATE_STOP, OSPC_RFORMAT_OSP, OSPC_RVENDOR_FREESWITCH);
-
-       info = (osp_threadarg_t *)malloc(sizeof(osp_threadarg_t));
-       info->transaction = transaction;
-       info->transid = cookie->transid;
-       info->cause = usage->cause;
-       info->start = cookie->start / 1000000;
-       info->alert = usage->alert / 1000000;
-       info->connect = usage->connect / 1000000;
-       info->end = usage->end / 1000000;
-       info->duration = usage->duration / 1000000;
-       info->pdd = usage->pdd / 1000;
-       info->release = usage->release;
-
-       OSPM_THRATTR_INIT(threadattr, error);
-       OSPM_SETDETACHED_STATE(threadattr, error);
-       OSPM_CREATE_THREAD(threadid, &threadattr, osp_report_thread, info, error);
-       OSPM_THRATTR_DESTROY(threadattr);
-
-       /* transaction and info will be released by osp_report_thread */
-
-       return status;
-}
-
-/*
- * Log UsageInd parameters
- * param cookie Cookie
- * param usage Usage info
- * return
- */
-static void osp_log_usageind(
-       osp_cookie_t *cookie,
-       osp_usage_t *usage)
-{
-       if (osp_globals.debug) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, osp_globals.loglevel,
-                       "UsageInd: "
-                       "transid '%"PRIu64"' "
-                       "destcount '%d' "
-                       "callid '%s' "
-                       "calling '%s' "
-                       "called '%s' "
-                       "srcdev '%s' "
-                       "dest '%s' "
-                       "nid '%s/%s' "
-                       "protocol '%s/%s' "
-                       "cause '%d' "
-                       "release '%s' "
-                       "times '%"PRId64"/%"PRId64"/%"PRId64"/%"PRId64"' "
-                       "duration '%"PRId64"' "
-                       "pdd '%"PRId64"' "
-                       "outsessionid '%s' "
-                       "codec '%s/%s' "
-                       "rtpctets '%d/%d' "
-                       "rtppackets '%d/%d'\n",
-                       cookie->transid,
-                       cookie->destcount,
-                       usage->callid,
-                       cookie->calling,
-                       cookie->called,
-                       cookie->srcdev,
-                       cookie->dest,
-                       osp_filter_null(cookie->srcnid), osp_filter_null(cookie->destnid),
-                       osp_get_protocol(usage->inprotocol), osp_get_protocol(usage->outprotocol),
-                       usage->cause,
-                       usage->release ? "term" : "orig",
-                       cookie->start / 1000000, usage->alert / 1000000, usage->connect / 1000000, usage->end / 1000000,
-                       usage->duration / 1000000,
-                       usage->pdd / 1000000,
-                       usage->callid,
-                       osp_filter_null(usage->srccodec), osp_filter_null(usage->destcodec),
-                       usage->rtpsrcrepoctets, usage->rtpdestrepoctets,
-                       usage->rtpsrcreppackets, usage->rtpdestreppackets);
-       }
+       OSP_DEBUG_END;
 }
 
 /*
@@ -2378,30 +2610,40 @@ static switch_status_t osp_on_reporting(
        switch_core_session_t *session)
 {
        switch_channel_t *channel;
-       osp_cookie_t cookie;
-       osp_usage_t usage;
        switch_caller_profile_t *originator;
-       switch_status_t status = SWITCH_STATUS_SUCCESS;
+       switch_caller_profile_t *terminator;
+       osp_results_t results;
+       switch_status_t status = SWITCH_STATUS_FALSE;
 
-       if (osp_globals.shutdown) {
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "OSP application inavailable\n");
-               return status;
-       }
+       OSP_DEBUG_START;
 
-       /* Only report for B-leg */
-       if (!(channel = switch_core_session_get_channel(session)) || !(originator = switch_channel_get_originator_caller_profile(channel))) {
-               return status;
-       }
+       if (osp_global.shutdown) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OSP application inavailable\n");
+       } else if (!(channel = switch_core_session_get_channel(session))) {
+               /* Make sure there is a valid channel when starting the OSP application */
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to find origiantor channel\n");
+       } else if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
+               /* A-leg */
+               OSP_DEBUG_MSG("A-leg");
 
-       if (osp_get_cookie(channel, &cookie) != SWITCH_STATUS_SUCCESS) {
-               return status;
-       }
+               /* Get originator profile */
+               if ((originator = switch_channel_get_caller_profile(channel))) {
+                       /* Get terminator profile, may be NULL */
+                       terminator = switch_channel_get_originatee_caller_profile(channel);
 
-       osp_get_usage(channel, originator, &cookie, &usage);
+                       memset(&results, 0, sizeof(osp_results_t));
+                       results.transaction = OSP_INVALID_HANDLE;
+                       results.protocol = OSPC_PROTNAME_UNKNOWN;
 
-       osp_log_usageind(&cookie, &usage);
+                       /* Do OSP usage report */
+                       status = osp_do_report(channel, originator, terminator, &results);
+               }
+       } else {
+               /* B-leg */
+               OSP_DEBUG_MSG("B-leg");
+       }
 
-       osp_report_usage(&cookie, &usage);
+       OSP_DEBUG_END;
 
        return status;
 }
@@ -2409,7 +2651,7 @@ static switch_status_t osp_on_reporting(
 /*
  * OSP module state handlers
  */
-static switch_state_handler_table_t state_handlers = {
+static switch_state_handler_table_t osp_handlers = {
        NULL,                           /*.on_init */
        NULL,                           /*.on_routing */
        NULL,                           /*.on_execute */
@@ -2423,59 +2665,53 @@ static switch_state_handler_table_t state_handlers = {
        osp_on_reporting        /*.on_reporting */
 };
 
+/* switch_status_t mod_osp_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)  */
+SWITCH_MODULE_LOAD_FUNCTION(mod_osp_load);
+/* switch_status_t mod_osp_shutdown(void) */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_osp_shutdown);
+/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) */
+SWITCH_MODULE_DEFINITION(mod_osp, mod_osp_load, mod_osp_shutdown, NULL);
+
 /*
+ * Called when OSP module is loaded
  * Macro expands to:
  * switch_status_t mod_osp_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
  */
 SWITCH_MODULE_LOAD_FUNCTION(mod_osp_load)
 {
-       switch_api_interface_t *api_interface;
+       switch_api_interface_t *cli_interface;
        switch_application_interface_t *app_interface;
-       switch_dialplan_interface_t *dp_interface;
        switch_status_t status = SWITCH_STATUS_SUCCESS;
 
-       /* Load OSP configuration */
-       if ((status = osp_load_settings(pool)) != SWITCH_STATUS_SUCCESS) {
+       OSP_DEBUG_START;
+
+       /* Load OSP module configuration */
+       if ((status = osp_load_config(pool)) != SWITCH_STATUS_SUCCESS) {
+               OSP_DEBUG_END;
                return status;
        }
 
-       /* Init OSP Toolkit */
+       /* Init OSP client end */
        osp_init_osptk();
 
-       /* Connect OSP internal structure to the blank pointer passed to OSP module */
+       /* Connect OSP module internal structure to the blank pointer passed to OSP module */
        *module_interface = switch_loadable_module_create_module_interface(pool, modname);
 
-       /* Add CLI OSP command */
-       SWITCH_ADD_API(api_interface, "osp", "OSP", osp_api_function, "status");
+       /* Add CLI osp status command */
+       SWITCH_ADD_API(cli_interface, "osp", "OSP", osp_cli_function, "status");
        switch_console_set_complete("add osp status");
 
-       /* Add OSP application */
-       SWITCH_ADD_APP(app_interface, "osp", "Perform an OSP lookup", "Perform an OSP lookup", osp_app_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
+       /* Add OSP module applications */
+       SWITCH_ADD_APP(app_interface, "osplookup", "Perform an OSP lookup", "Perform an OSP lookup", osp_lookup_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
+       SWITCH_ADD_APP(app_interface, "ospnext", "Retrive next OSP route", "Retrive next OSP route", osp_next_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
 
-       /* Add OSP dialplan */
-       SWITCH_ADD_DIALPLAN(dp_interface, "osp", osp_dialplan_function);
+       /* Add OSP module state handlers */
+       switch_core_add_state_handler(&osp_handlers);
 
-       /* Add OSP state handlers */
-       switch_core_add_state_handler(&state_handlers);
+       OSP_DEBUG_END;
 
        /* Indicate that the module should continue to be loaded */
-       return status;
-}
-
-/*
- * Cleanup OSP client end
- * return
- */
-static void osp_cleanup_osptk(void)
-{
-       osp_profile_t *profile;
-
-       for (profile = osp_profiles; profile; profile = profile->next) {
-               OSPPProviderDelete(profile->provider, 0);
-               profile->provider = OSP_INVALID_HANDLE;
-       }
-
-       OSPPCleanup();
+       return SWITCH_STATUS_SUCCESS;
 }
 
 /*
@@ -2485,14 +2721,18 @@ static void osp_cleanup_osptk(void)
  */
 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_osp_shutdown)
 {
+       OSP_DEBUG_START;
+
        /* Shutdown OSP module */
-       osp_globals.shutdown = SWITCH_TRUE;
+       osp_global.shutdown = SWITCH_TRUE;
 
-       /* Cleanup OSP Toolkit */
+       /* Cleanup OSP client end */
        osp_cleanup_osptk();
 
-       /* Remoeve OSP state handlers */
-       switch_core_remove_state_handler(&state_handlers);
+       /* Remoeve OSP module state handlers */
+       switch_core_remove_state_handler(&osp_handlers);
+
+       OSP_DEBUG_END;
 
        return SWITCH_STATUS_SUCCESS;
 }