H350 module
Admin Guide
Section titled “Admin Guide”Overview
Section titled “Overview”The OpenSIPS H350 module enables an OpenSIPS SIP proxy server to access SIP account data stored in an LDAP RFC4510 directory containing H.350 H350 commObjects. ITU-T Recommendation H.350 standardizes LDAP object classes to store Real-Time Communication (RTC) account data. In particular, H.350.4 H350 4 defines an object class called sipIdentity that includes attribute specifications for SIP account data like SIP URI, SIP digest username/password, or service level. This allows to store SIP account data in a vendor neutral way and lets different entities, like SIP proxies, provisioning, or billing applications, access the data in a standardized format.
The ViDe H.350 Cookbook vide H350 cookbook is a good reference for deploying an H.350 directory. Besides general information on H.350, LDAP, and related standards, this document explains how to set up an H.350/LDAP directory and discusses different deployment scenarios.
The H350 module uses the OpenSIPS LDAP module to import H.350 attribute values into the OpenSIPS routing script variable space. The module exports functions to parse and store the H.350 attribute values from the OpenSIPS routing script. It allows a script writer to implement H.350 based SIP digest authentication, call forwarding, SIP URI alias to AOR rewriting, and service level parsing.
Example H.350 commObject LDAP Entry
Section titled “Example H.350 commObject LDAP Entry”The following example shows a typical H.350 commObject LDAP entry storing SIP account data.
Attribute Name Attribute Value(s)-------------- -----------------
# LDAP URI identifying the owner of this commObject, typically# points to an entry in the enterprise directorycommOwner ldap://dir.example.com/dc=example,dc=com??one?(uid=bob)
# Unique identifier for this commObject, used for referencing# this object e.g. from the enterprise directorycommUniqueId 298217asdjgj213
# Determines if this commObject should be listed on white pagescommPrivate false
# Valid SIP URIs for this account (can be used to store alias SIP URIs# like DIDs as well)SIPIdentitySIPURI sip:bob@example.com sip:bob@alias.example.com sip:+1919123456@alias.example.com# SIP digest usernameSIPIdentityUserName bob
# SIP digest passwordSIPIdentityPassword pwd
# SIP proxy addressSIPIdentityProxyAddress sip.example.com
# SIP registrar addressSIPIdentityRegistrarAddress sip.example.com
# Call preferences: Forward to voicemail on no response# after 20 seconds and on busycallPreferenceURI sip:bob@voicemail.example.com n:20000 sip:bob@voicemail.example.com b
# Account service level(s)SIPIdentityServiceLevel long_distance conferencing
# H.350 object classesobjectClass top commObject SIPIdentity callPreferenceURIObjectDependencies
Section titled “Dependencies”OpenSIPS Modules
Section titled “OpenSIPS Modules”The module depends on the following modules (the listed modules must be loaded before this module):
- LDAP
External Libraries or Applications
Section titled “External Libraries or Applications”The following libraries or applications must be installed before running OpenSIPS with this module loaded:
- OpenLDAP library (libldap), libldap header files (libldap-dev) are needed for compilation
Exported Parameters
Section titled “Exported Parameters”ldap_session (string)
Section titled “ldap_session (string)”Name of the LDAP session to be used for H.350 queries, as defined in the LDAP module configuration file.
Default value: ""
modparam("h350", "ldap_session", "h350");base_dn (string)
Section titled “base_dn (string)”Base LDAP DN to start LDAP search for H.350 entries. For best performance, this should be set to the direct ancestor of the H.350 objects.
Default value: ""
modparam("h350", "base_dn", "ou=h350,dc=example,dc=com");search_scope (string)
Section titled “search_scope (string)”LDAP search scope for H.350 queries, one of “one”, “base”, or “sub”.
Default value: “one”
modparam("h350", "search_scope", "sub");Exported Functions
Section titled “Exported Functions”h350_sipuri_lookup(sip_uri)
Section titled “h350_sipuri_lookup(sip_uri)”This function performs an LDAP search query for an H.350 commObject with a SIPIdentitySIPURI of sip_uri. The sip_uri parameter first gets escaped according the rules for LDAP filter strings. The result of the LDAP search is stored internally and can be accessed either by one of the h350_result* or one of the ldap_result* functions from the OpenSIPS LDAP module.
The function returns -1 (FALSE) for internal errors, and -2 (FALSE) if no H.350 commObject was found with a matching sip_uri. n > 0 (TRUE) is returned if n H.350 commObjects were found.
This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE.
sip_uri (string)
H.350 SIPIdentitySIPURI to search for in directory.
n > 0 (TRUE):
nH.350 commObjects found.
-1 (FALSE):
- Internal error occurred.
-2 (FALSE):
- No H.350 commObject found.
This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE.
## H.350 lookup for callee#
if (!h350_sipuri_lookup("sip:$rU@$rd")){ switch ($retcode) { case -2: xlog("L_INFO", "h350 callee lookup: no entry found in H.350 directory"); exit; case -1: sl_send_reply(500, "Internal server error"); exit; }}
# now h350_result* or ldap_result* functions can be usedh350_auth_lookup(auth_username, “username_avp_spec/pwd_avp_spec”)
Section titled “h350_auth_lookup(auth_username, “username_avp_spec/pwd_avp_spec”)”This function performs an LDAP search query for SIP digest authentication credentials in an H.350 directory. The H.350 directory is searched for a commObject with SIPIdentityUserName of auth_username. If such a commObject is found, the SIP digest authentication username and password are stored in AVPs username_avp_spec and pwd_avp_spec, respectively. pv__authorize* functions from AUTH module can then be used to perform SIP digest authentication.
The function returns 1 (TRUE) if an H.350 commObject was found, -1 (FALSE) in case of an internal error, and -2 (FALSE) if no matching commObject was found.
This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE.
auth_username (string)
H.350 SIPIdentityUserName to search for in directory.
username_avp_spec (var)
Specification for authentication username AVP, e.g. $avp(username).
pwd_avp_spec (var)
Specification for authentication password AVP, e.g. $avp(pwd).
1 (TRUE):
- H.350 commObject found and SIP digest authentication credentials stored in
username_avp_specandpwd_avp_spec.
-1 (FALSE):
- Internal error occurred.
-2 (FALSE):
- No H.350 commObject found.
This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE.
# -- auth params --modparam("auth", "username_spec", "$avp(auth_user)")modparam("auth", "password_spec", "$avp(auth_pwd)")modparam("auth", "calculate_ha1", 1)
# -- h350 params --modparam("h350", "ldap_session", "h350")modparam("h350", "base_dn", "ou=h350,dc=example,dc=com")modparam("h350", "search_scope", "one")
route[1]{ # # H.350 based SIP digest authentication #
# challenge all requests not including an Auth header if (!(is_present_hf("Authorization") || is_present_hf("Proxy-Authorization"))) { if (is_method("REGISTER")) { www_challenge("example.com", 0); exit; } proxy_challenge("example.com", 0); exit; }
# get digest password from H.350 using auth username ($au) if (!h350_auth_lookup($au, "$avp(auth_user)/$avp(auth_pwd)")) { switch ($retcode) { case -2: sl_send_reply(401, "Unauthorized"); exit; case -1: sl_send_reply(500, "Internal server error"); exit; } }
# REGISTER requests if (is_method("REGISTER")) { if (!pv_www_authorize("example.com")) { if ($retcode == -5) { sl_send_reply(500, "Internal server error"); exit; } else { www_challenge("example.com", 0); exit; } }
consume_credentials(); xlog("L_INFO", "REGISTER request successfully authenticated"); return(1); }
# non-REGISTER requests if (!pv_proxy_authorize("example.com")) { if ($retcode == -5) { sl_send_reply(500, "Internal server error"); exit; } else { proxy_challenge("example.com", 0); exit; } }
consume_credentials(); xlog("L_INFO", "$rm request successfully authenticated"); return(1);}h350_result_call_preferences(avp_name_prefix)
Section titled “h350_result_call_preferences(avp_name_prefix)”This function parses the callPreferenceURI attribute of an H.350 commObject, which must have been fetched through h350__lookup* or ldap_search. callPreferenceURI is a multi-valued attribute that stores call preference rules like e.g. forward-on-busy or forward-unconditionally. Directory services architecture for call forwarding and preferences H350 6 defines a format for simple call forwarding rules:
target_uri type[:argument]
In a SIP environment, target_uri is typically the call forwarding rule’s target SIP URI, although it could be any type of URI, e.g. an HTTP pointer to a CPL script. Four different values are specified for type: b for “forward on busy”, n for “forward on no answer”, u for “forward unconditionally”, and f for “forward on destination not found”. The optional argument is a string indicating the time in milliseconds after which the call forwarding should occur.
# Example 1:# forward to sip:voicemail@example.com on no answer after 15 seconds:
callPreferenceURI: sip:voicemail@example.com n:15000
# Example 2:# unconditionally forward to sip:alice@example.com:
callPreferenceURI: sip:alice@example.com u
# Example 3:# forward to sip:bob@example.com and sip:alice@example.com# (forking) on destination not found:
callPreferenceURI: sip:bob@example.com fcallPreferenceURI: sip:alice@example.com fh350_result_call_preferences stores these call forwarding rules as AVPs according to the following rules:
## AVP storing a forwarding rule's target URI#
AVP name = avp_name_prefix + '_' + typeAVP value = target_uri
## AVP storing a forwarding rule's argument#
AVP name = avp_name_prefix + '_' + type + '_t'AVP value = argument / 1000Example 1 from above would result in two AVPs: $avp("prefix_n") = "sip:voicemail@example.com" and $avp("prefix_n_t") = 15.
Example 2: $avp("prefix_u") = "sip:alice@example.com".
Example 3: $avp("prefix_f[1]") = "sip:bob@example.com" and $avp("prefix_f[2]]") = "sip:alice@example.com".
These AVPs can then be used to implement the desired behavior in the OpenSIPS routing script.
This function returns the number of successfully parsed simple call forwarding rules (TRUE), in case the H.350 callPreferenceURI attribute contained one or multiple values matching the simple call forwarding rule syntax described above. It returns -1 (FALSE) for internal errors, and -2 (FALSE) if none of the rules matched or if no callPreferenceURI attribute was found.
This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE.
avp_name_prefix (string)
Name prefix for call forwarding rule AVPs, as described above.
n > 0 (TRUE):
nsimple call forwarding rules found.
-1 (FALSE):
- Internal error occurred.
-2 (FALSE):
- No simple call forwarding rule found, or callPreferenceURI not present.
This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE.
## H.350 lookup for callee#
... h350_sipuri_lookup("sip:$rU@$rd") ...
## store H.350 call preferences in AVP#
if (!h350_result_call_preferences("callee_pref_") && ($retcode == -1)){ sl_send_reply(500, "Internal server error"); exit;}
# $avp(callee_pref_u) == CFU URI(s)# $avp(callee_pref_n) == CFNR URI(s)# $avp(callee_pref_n_t) == CFNR timeout in seconds# $avp(callee_pref_b) == CFB URI(s)# $avp(callee_pref_f) == CFOFFLINE URI(s)
## Example for forward-unconditionally (CFU)#
if ($avp(callee_pref_u) != NULL){ # push CFU URI into R-URI and additional branches # --> request can fork $ru = $avp(callee_pref_u); $avp(callee_pref_u) = NULL; while ($avp(callee_pref_u)!=NULL) { $branch = $avp(callee_pref_u); $avp(callee_pref_u) = NULL; } sl_send_reply(181, "Call is being forwarded"); t_relay(); exit;}h350_result_service_level(avp_name_prefix)
Section titled “h350_result_service_level(avp_name_prefix)”Directory services architecture for SIP H350 4 defines a multi-valued LDAP attribute named SIPIdentityServiceLevel, which can be used to store SIP account service level values in an LDAP directory. This function parses the SIPIdentityServiceLevel attribute and stores all service level values as AVPs for later retrieval in the OpenSIPS routing script. The function accesses the H.350 commObject fetched by a call to h350__lookup* or ldap_search.
The resulting AVPs have a name of the form avp_name_prefix + SIPIdentityServiceLevel attribute value, and an integer value of 1.
SIPIdentityServiceLevel: longdistanceSIPIdentityServiceLevel: internationalSIPIdentityServiceLevel: 900
after calling h350_result_service_level("sl_"), the following AVPswill be available in the routing script:
$avp("sl_longdistance") = 1$avp("sl_international") = 1$avp("sl_900") = 1This function returns the number of added AVPs (TRUE), -1 (FALSE)for internal errors, and -2 (FALSE)if no SIPIdentityServiceLevel attribute was found.
The function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE.
avp_name_prefix (string)
Name prefix for service level AVPs, as described above.
n > 0 (TRUE):
nAVPs added.
-1 (FALSE):
- Internal error occurred.
-2 (FALSE):
- No SIPIdentityServiceLevel attribute found.
This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, and ONREPLY_ROUTE.
## H.350 SIP digest authentication for caller#
... h350_auth_lookup("$au", ...) ...
## store caller's service level as AVP#
if (!h350_result_service_level("caller_sl_") && ($retcode == -1)){ sl_send_reply(500, "Internal server error"); exit;}
## make routing decision based on service level AVPs#
if ($avp(caller_sl_international) != NULL){ t_relay();}else { sl_send_reply(403, "Forbidden");}exit;Contributors
Section titled “Contributors”By Commit Statistics
Section titled “By Commit Statistics”Top contributors by DevScore(1), authored commits(2) and lines added/removed(3)
| # | Name | DevScore | Commits | Lines++ | Lines— |
|---|---|---|---|---|---|
| 1. | Christian Schlatter | 21 | 3 | 1993 | 1 |
| 2. | Bogdan-Andrei Iancu (@bogdan-iancu) | 17 | 14 | 67 | 67 |
| 3. | Daniel-Constantin Mierla (@miconda) | 12 | 9 | 85 | 89 |
| 4. | Liviu Chircu (@liviuchircu) | 12 | 9 | 77 | 91 |
| 5. | Razvan Crainea (@razvancrainea) | 11 | 8 | 56 | 64 |
| 6. | Stefan Darius | 11 | 4 | 604 | 12 |
| 7. | Vlad Patrascu (@rvlad-patrascu) | 9 | 3 | 83 | 250 |
| 8. | Maksym Sobolyev (@sobomax) | 4 | 2 | 3 | 4 |
| 9. | Konstantin Bokarius | 3 | 1 | 2 | 5 |
| 10. | Peter Lemenkov (@lemenkov) | 3 | 1 | 1 | 1 |
All remaining contributors: Edson Gellert Schubert.
(1) DevScore = author_commits + author_lines_added / (project_lines_added / project_commits) + author_lines_deleted / (project_lines_deleted / project_commits)
(2) including any documentation-related commits, excluding merge commits
(3) ignoring whitespace edits, renamed files and auto-generated files
By Commit Activity
Section titled “By Commit Activity”| # | Name | Commit Activity |
|---|---|---|
| 1. | Stefan Darius | Jun 2026 - Jul 2026 |
| 2. | Razvan Crainea (@razvancrainea) | Jun 2011 - Jun 2026 |
| 3. | Liviu Chircu (@liviuchircu) | Mar 2014 - May 2024 |
| 4. | Bogdan-Andrei Iancu (@bogdan-iancu) | Jan 2008 - Feb 2024 |
| 5. | Maksym Sobolyev (@sobomax) | Feb 2023 - Feb 2023 |
| 6. | Vlad Patrascu (@rvlad-patrascu) | May 2017 - Apr 2019 |
| 7. | Peter Lemenkov (@lemenkov) | Jun 2018 - Jun 2018 |
| 8. | Daniel-Constantin Mierla (@miconda) | Sep 2007 - Mar 2008 |
| 9. | Konstantin Bokarius | Mar 2008 - Mar 2008 |
| 10. | Edson Gellert Schubert | Feb 2008 - Feb 2008 |
All remaining contributors: Christian Schlatter.
(1) including any documentation-related commits, excluding merge commits
Documentation
Section titled “Documentation”Contributors
Section titled “Contributors”Last edited by: Razvan Crainea (@razvancrainea), Bogdan-Andrei Iancu (@bogdan-iancu), Vlad Patrascu (@rvlad-patrascu), Peter Lemenkov (@lemenkov), Liviu Chircu (@liviuchircu), Daniel-Constantin Mierla (@miconda), Konstantin Bokarius, Edson Gellert Schubert, Christian Schlatter.
License
Section titled “License”All documentation files (i.e. .md extension) are licensed under the Creative Common License 4.0