Configuration for the ienum openser 1. Introduction Using Infrastructure ENUM and SIP VoIP peering requires 2 steps: 1. perform IENUM lookups 2. interconnect with the destination published in the IENUM NAPTRs Typically, a VoIP setup has a central node which performs the routing logic. I will call this the "main proxy". We can add the previously described steps in the main proxy or use dedicated nodes for doing the IENUM lookup and session peering. This has many advantages regarding security, flexibility and reliability. We suggest a setup of a dedicated "border proxy" which performs the IENUM lookup and takes care of peering (e.g. authentication of peering partners). This border proxy is a logical node, that means it can be installed on the same PC as the main proxy. Nevertheless doing the IENUM lookup and the peering in the main proxy. If you want to do this, please use the ideas and config snippets from our suggested scenario (a dedicated border proxy) to adopt your main proxy's routing logic. 2. The Border Proxy The border proxy takes care of the IENUM lookup and the SIP peering. The border proxy will perform the following steps: For outgoing calls: - bring number into E.164 format - perform IENUM lookup - perform ENUM lookup (optional) - if no destination is found in IENUM/ENUM, send an apropriate SIP response back to the main proxy. The main proxy will then perform the default routing (usually sends the call to the PSTN gateway) - perform a domain policy lookup. This step detects if the destination found in IENUM will accept SIP calls from us - apply destination domain's peering policy (e.g. set proper TLS parameters, send to certain port, ...) - add P-Asserted-Idendity: Header and Privacy Header the send proper CLI and CLIP/CLIR - activate accounting - send the call to the destination For incoming calls: - receive call - authenticate sender (e.g. TLS or IP address) - activate accounting - adjust CLI to fit local dialplan (e.g reformat P-Asserted-Idendity header) - forward the call to the main proxy 3. Configuration 3.1. Main Proxy The main proxy should forward the call to the border proxy for IENUM lookup and peering. If the main proxy receives a certain response code, it will failover to the standard route to the PSTN. E.g. if the main proxy is (open)ser based, the respective config snippets will be: route{ # all the authentication and NAT traversal stuff ... # user is authenticated. check if destination is an phone number if (uri =~ "^sip:(\+|\*)?[0-9]+@") { # PSTN number detected if (uri=~"^sip:[1-9]") { xlog("L_WARN", "number normalization: illegal number, 404..."); sl_send_reply("404", "Not found - illegal number"); exit; } if (uri=~"^sip:0[1-9]") { xlog("L_INFO","Austria national number detected, remove 0, prefix +43\n"); strip(1); prefix("+43"); } else if (uri=~"^sip:00[1-9]") { xlog("L_INFO","international number detected, remove 00, prefix +\n"); strip(2); prefix("+"); } else if (uri=~"^sip:000") { xlog("L_INFO", "000 detected, illegal number, 404..."); sl_send_reply("404", "Not found - illegal number"); exit; } } # usually calls sent to the gateway have an Remote-Party-ID header # to signal the CLI to the gateway. We send this RPID header also to the # border proxy # create new AVP with the Remote-Party-ID header avp_printf("$avp(s:temp)","$avp(s:rpid);party=calling;id-type=subscriber;screen=yes"); append_hf("Remote-Party-ID: $avp(s:temp)\r\n"); # now, usually we would send the call to the PSTN gateway. But instead # we activate a failure route and send it to the border proxy t_on_failure("1"); # debpending on your domain setup you may need to rewrite the domain # to address the border proxy # rewritehostport("bp1.istp1.com"); # send the request to the border proxy if (!t_relay()) { sl_reply_error(); }; exit; } failure_route[1] { # handles error on border proxy xlog("L_INFO","failure_route(1): reply status = $rs\n"); # if caller cancels the call we will also enter the failure route if(t_was_cancelled()) { xlog("L_INFO","caller cancelled call ...exit\n"); exit; } # border proxy will return "499" in case of unsuccessful IENUM/domainpolicy lookup if(t_check_status("499")) { xlog("L_INFO","callee neither in I-ENUM nor in U-ENUM...sending to PSTN Gateway..."); rewritehostport("gateway.istp1.com"); t_relay(); exit; } xlog("L_INFO","send down error code from border proxy to user...exit\n"); exit; } If the main proxy is Asterisk based, the respective snippet may look like this (untested): [frompbx] ; border proxy will send "503" if no IENUM result found ; in this example the Asterisk is also the gateway. If not you ; have to adopt the second Dial parameter to send the call to your ; gateway exten => _[1-9].,1,Dial(SIP/+431${EXTEN}@bp1.itsp1.com,90) exten => _[1-9].,2,GotoIf($["${DIALSTATUS}" = "CONGESTION"]?103:3) exten => _[1-9].,3,Hangup exten => _[1-9].,103,Dial(ZAP/g1/${EXTEN},90) exten => _0[1-9].,1,Dial(SIP/+43${EXTEN:1}@bp1.itsp1.com,90) exten => _0[1-9].,2,GotoIf($["${DIALSTATUS}" = "CONGESTION"]?103:3) exten => _0[1-9].,3,Hangup exten => _0[1-9].,103,Dial(ZAP/g1/${EXTEN},90) exten => _00[1-9].,1,Dial(SIP/+${EXTEN:2}@bp1.itsp1.com,90) exten => _00[1-9].,2,GotoIf($["${DIALSTATUS}" = "CONGESTION"]?103:3) exten => _00[1-9].,3,Hangup exten => _00[1-9].,103,Dial(ZAP/g1/${EXTEN},90) 3.2 Border Proxy The border proxy performs the IENUM/ENUM lookups and the domainpolicy lookup. Further it may perform authentication for peering purposes. Again some config snippets for openser (you need the patched ienum-openser from www.enum.at) ...config section ...module parameter section loadmodule "/usr/lib/openser/modules/enum.so" loadmodule "/usr/lib/openser/modules/domainpolicy.so" loadmodule "/usr/lib/openser/modules/tlsops.so" # carrier enum modparam("enum", "i_enum_suffix", "e164.arpa.") modparam("enum", "bl_algorithm", "c") # domainpolicy module (this must be named like in the domainpolicy table) modparam("domainpolicy", "port_override_avp", "portoverride") modparam("domainpolicy", "transport_override_avp", "transportoverride") modparam("domainpolicy", "domain_prefix_avp", "domainprefix") modparam("domainpolicy", "domain_suffix_avp", "domainsuffix") modparam("domainpolicy", "send_socket_avp", "sendsocket") route{ ..... # now, we split routing depending on the direction # the protocol can be used to identify the direction # if we require TCP or TLS for peering, but use UDP inside # we can use the protocol to detect the call direction # tls: request from peering partner # udp,tcp: local request if( (proto==tls) or (proto==tcp) ){ route(2); } else { route(3); }; } route[2] { # TLS requests - thus requests received from peering partners. # This requests are already authenticated via TLS certificate # validation, thus no further SIP based authentication needed. # Just forward the requests to the main proxy. # Beforehand, we map from the anonymous IENUM SIP URIs to the # user's AoR using the dbaliases module. Note: this mapping can # also be done in the main proxy. xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(2): TLS requests\n"); if (alias_db_lookup("dbaliases")) { xlog("L_INFO","dbaliases lookup successful, new URI = $ru\n"); avp_write("s:outbound.itsp1.ienum.labs.nic.at","$avp(i:679)"); avp_pushto("$du","$avp(i:679)"); # route(5); # optional process received P-Asserted_Identiy header exit; } xlog("L_INFO","dbaliases lookup failed ... 404\n"); sl_send_reply("404","Unknown user"); exit; } route[3] { # UDP/TCP - thus requests received from the local main proxy. # This requests are already authenticated by the main proxy, # thus no further SIP based authentication needed. # # Perform the following steps: # 1. Try I-ENUM lookup # 2. Try U-ENUM lookup # 3. reply with 499 xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(3): UDP/TCP requests\n"); if ( i_enum_query() ) { xlog("L_INFO","I-ENUM lookup successful, new URI = $ru, check if useful...\n"); if (uri == myself) { xlog("L_INFO","I-ENUM SIP URI belongs to myself, relay back to main proxy...\n"); sl_send_reply("100","I-ENUM succeeded, number belongs to us, sending back to main proxy..."); route(2); exit; } if (dp_can_connect()) { # check the domainpolicy of the destination xlog("L_INFO","dp_can_connect succeeded:\n"); xlog("L_INFO"," port_override_avp = '$avp(s:portoverride)'\n"); xlog("L_INFO"," transport_override_avp = '$avp(s:transportoverride)'\n"); xlog("L_INFO"," domain_prefix_avp = '$avp(s:domainprefix)'\n"); xlog("L_INFO"," domain_suffix_avp = '$avp(s:domainsuffix)'\n"); xlog("L_INFO"," send_socket_avp = '$avp(s:sendsocket)'\n"); xlog("L_INFO"," tls_client_domain_avp = '$avp(i:400)'\n"); # apply domain policy if (dp_apply_policy()) { xlog("L_INFO","I-ENUM SIP URI is a known peering partner, use this uri and relay request...\n"); xlog("L_INFO"," R-URI = $ru\n"); xlog("L_INFO"," d-URI = $du\n"); sl_send_reply("100","I-ENUM und domainpolicy succeeded, sending to other domain..."); route(4); exit; } xlog("L_INFO","dp_apply_policy failed\n"); } else { xlog("L_INFO","dp_apply_policy failed\n"); } } else { xlog("L_INFO","I-ENUM lookup failed, fallback to U-ENUM ...\n"); } revert_uri(); if ( enum_query() ) { xlog("L_INFO","U-ENUM lookup successful, new URI = $ru, relay request...\n"); sl_send_reply("100","U-ENUM succeeded, sending to other domain..."); route(4); exit; } xlog("L_INFO","I-ENUM lookup failed, send 499 back to main proxy ...\n"); sl_send_reply("499","Number not in ENUM or domainpolicy failed"); exit; } route[4]{ # This route block reformats the rpid from the internal number format # to the number format used in the I-ENUM trial. # Thus, the proxy will take the existing rpid information (Remote-Party-ID header # or P-Asserted_Identity header) and build an P-Asserted-Identity header with # a tel URI. xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(4): add P-Asserted-Identity header\n"); # write the complete content of the Remote-Party-ID header into an AVP append_hf("Remote-Party-ID: $avp(s:rpidheader)\r\n"); # replace the sip uri with a tel uri # \1 \2 \3 \4 avp_subst("$avp(s:rpidheader)", "/^(.*)(.*)/tel:\2/gi"); # remove the Remote-Party-ID header and create the P-Asserted-Identity header remove_hf("Remote-Party-ID"); append_hf("P-Asserted-Identity: $avp(s:rpidheader)\r\n"); route(1); exit; } 4. Appendix 4.1. Full Config of a Main Proxy # # $Id: openser.cfg,v 1.5 2005/10/28 19:45:33 bogdan_iancu Exp $ # # simple quick-start config script # # Austrian I-ENUM Trial # configuration of main proxy # ----------- global configuration parameters ------------------------ debug=3 # debug level (cmd line: -dddddddddd) fork=yes #log_stderror=yes # (cmd line: -E) /* Uncomment these lines to enter debugging mode fork=no log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) children=4 fifo="/tmp/openser_fifo" # syslog logging, configure in /etc/syslog log_facility=LOG_LOCAL0 # /var/log/openser.log server_header ="server_header: itsp1, main proxy" user_agent_header="user_agent_header: itsp1, main proxy" listen=udp:10.10.0.41:5060 listen=tcp:10.10.0.41:5060 listen=tls:10.10.0.41:5061 # our domain alias=itsp1.ienum.labs.nic.at # for addressing via outbound proxy and pre loaded route set # (otherwise will is_myself in loose_route not match) alias=outbound.itsp1.ienum.labs.nic.at # # uncomment the following lines for TLS support #disable_tls = 0 #listen = tls:your_IP:5061 #tls_verify = 1 #tls_require_certificate = 1 #tls_method = TLSv1 #tls_certificate = "/etc/openser/tls/user/user-cert.pem" #tls_private_key = "/etc/openser/tls/user/user-privkey.pem" #tls_ca_list = "/etc/openser/tls/user/user-calist.pem" # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "/usr/lib/openser/modules/mysql.so" loadmodule "/usr/lib/openser/modules/sl.so" loadmodule "/usr/lib/openser/modules/tm.so" loadmodule "/usr/lib/openser/modules/rr.so" loadmodule "/usr/lib/openser/modules/maxfwd.so" loadmodule "/usr/lib/openser/modules/usrloc.so" loadmodule "/usr/lib/openser/modules/registrar.so" loadmodule "/usr/lib/openser/modules/textops.so" loadmodule "/usr/lib/openser/modules/xlog.so" loadmodule "/usr/lib/openser/modules/avpops.so" loadmodule "/usr/lib/openser/modules/acc.so" loadmodule "/usr/lib/openser/modules/uri.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! loadmodule "/usr/lib/openser/modules/auth.so" loadmodule "/usr/lib/openser/modules/auth_db.so" loadmodule "/usr/lib/openser/modules/enum.so" loadmodule "/usr/lib/openser/modules/domain.so" # ----------------- setting module-specific parameters --------------- # read and write DB access modparam("acc" , "db_url", "mysql://openser:openserrw@127.0.0.1/openser") # accounting parameters modparam("acc", "early_media", 1) modparam("acc", "report_ack", 1) modparam("acc", "report_cancels", 0) modparam("acc", "multi_leg_enabled", 0) modparam("acc", "failed_transaction_flag", 4) # radius, syslog and DB modparam("acc", "log_flag", 1) # syslog modparam("acc", "db_flag", 2) # DB modparam("acc", "db_missed_flag", 3) # DB missed calles table # the following modules also supports multidomain, but are not used # by us: group, group_radius, speeddial, uri_db modparam("alias_db|usrloc|registrar|avpops", "use_domain", 1) # -- usrloc params -- # modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # modparam("auth_db", "calculate_ha1", yes) # # If you set "calculate_ha1" parameter to yes (which true in this config), # uncomment also the following parameter) # modparam("auth_db", "password_column", "password") modparam("auth_db|usrloc|registrar", "use_domain", 1) # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ------------------------- request routing logic ------------------- # main routing logic route{ xlog("L_ERR","[$Tf] [$si:$sp] $rm $ru (From: $fu -> To: $tu) request received....\n"); # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # mark transaction for accounting setflag(1); setflag(2); setflag(4); if ( is_method("CANCEL") && !t_check_trans() ) { # CANCEL without matching INVITE transaction, ignore # may happen if the INVITE is slower than the CANCEL # ignore the CANCEL, as the client will retransmit it, and maybe next time # the INVITE transaction is already created xlog("L_WARN","CANCEL without matching transaction ... ignore and discard.\n"); exit; } if ( is_method("CANCEL") ) { # CANCEL with matching INVITE transaction, just # t_relay xlog("L_INFO","CANCEL with matching transaction ... t_relay.\n"); t_relay(); exit; } # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { if (!has_totag()) { xlog("L_WARN","loose_route request without to-tag, 403...\n"); sl_send_reply("403", "out-of-dialog loose_route not allowed"); exit; } xlog("L_WARN","request loose_route processed...\n"); route(1); }; if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after a 487 xlog("L_INFO","local end-to-end ACK for an existent INVITE transaction detected ...t_relay()\n"); t_relay(); } else { xlog("L_WARN","ACK without matching transaction ... ignore and discard.\n"); } exit; } if ( has_totag() ) { # in-dialog requests should be handled by loose_route # this should be fixed in the client or in openser xlog("L_ERR","in-dialog request was not catched by loose_route block, 403... \n"); xlog("L_ERR","this is the complete message:\n"); xlog("L_ERR","$mb\n"); sl_send_reply("403","in-dialog request without loose_route is not allowed, this is a bug in the client or in this proxy\n"); exit; } # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol # # record route will be done after loose_route to do not change the # ftag parameter in the Route header (eyebeam) if (!method=="REGISTER") record_route(); if (!uri==myself) { xlog("L_WARN","outoing call ...\n"); route(1); }; # if the request is for our domain use UsrLoc # (in case, it does not work, use the following command # with proper names and addresses in it) if (uri==myself) { if (method=="REGISTER") { if (t_newtran()) { # Uncomment this if you want to use digest authentication if (!www_authorize("", "subscriber")) { www_challenge("", "0"); exit; }; save("location"); exit; } else sl_reply_error(); t_release(); }; if (is_method("INVITE")) { # Uncomment this if you want to use digest authentication if (from_uri == myself) { xlog("L_INFO","INVITE from local user, check auth\n"); if (!proxy_authorize("", "subscriber")) { xlog("L_INFO","auth failed...challenge\n"); proxy_challenge("", "0"); exit; }; consume_credentials(); # if avp rpid available add rpid header remove_hf("Remote-Party-ID"); remove_hf("P-Asserted-Identity"); if (is_avp_set("$avp(s:rpid)")) { xlog("L_INFO","RPID defined: rpid=$avp(s:rpid)\n"); #add Remote-Party-ID header # create new AVP with the Remote-Party-ID header avp_printf("$avp(s:temp)","$avp(s:rpid);party=calling;id-type=subscriber;screen=yes"); xlog("L_INFO", "Remote-Party-ID header: $avp(s:temp)"); append_hf("Remote-Party-ID: $avp(s:temp)\r\n"); # #add P-Asserted-Identity header # # create new AVP with the P-Asserted-Identity header # avp_printf("$avp(s:temp)","$avp(s:rpid)"); # xlog("L_INFO", "P-Asserted-Identity header: $avp(s:temp)"); # append_hf("$avp(s:temp)","P-Asserted-Identity"); } else { xlog("L_INFO","RPID not defined...\n"); } if (uri =~ "^sip:(\+|\*)?[0-9]+@") { # PSTN number detected if (uri=~"^sip:[1-9]") { xlog("L_WARN", "number normalization: illegal number, 404..."); sl_send_reply("404", "Not found - illegal number"); exit; } if (uri=~"^sip:0[1-9]") { xlog("L_INFO","Austria national number detected, remove 0, prefix +43\n"); strip(1); prefix("+43"); } else if (uri=~"^sip:00[1-9]") { xlog("L_INFO","international number detected, remove 00, prefix +\n"); strip(2); prefix("+"); } else if (uri=~"^sip:000") { xlog("L_INFO", "000 detected, illegal number, 404..."); sl_send_reply("404", "Not found - illegal number"); exit; } t_on_failure("1"); # rewritehostport("bp1.itsp1.ienum.labs.nic.at"); route(1); } if (!uri==myself) { append_hf("P-hint: Proxy1 outbound alias\r\n"); route(1); }; } else { xlog("L_INFO","incoming call...\n"); } } # native SIP destinations are handled using our USRLOC DB if (!lookup("location")) { sl_send_reply("404", "Not Found in location table"); exit; }; xlog("L_INFO","user found, route(1)...\n"); append_hf("P-hint: usrloc applied\r\n"); }; route(1); } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP xlog("L_INFO","t_relay....\n"); if (!t_relay()) { sl_reply_error(); }; exit; } failure_route[1] { # handles error on border proxy xlog("L_INFO","failure_route(1): reply status = $rs\n"); if(t_was_cancelled()) { xlog("L_INFO","caller cancelled call ...exit\n"); exit; } if(t_check_status("499")) { xlog("L_INFO","callee neither in I-ENUM nor in U-ENUM...sending to PSTN Gateway...404"); t_reply("404","no PSTN gateway configured"); exit; } xlog("L_INFO","send down error code from upstream proxy...exit\n"); exit; } route[5] { # dummy route for aliases lookup lookup("aliases"); } 4.2. Full Config of a Border Proxy # # $Id: openser.cfg,v 1.5 2005/10/28 19:45:33 bogdan_iancu Exp $ # # simple quick-start config script # # Austrian I-ENUM Trial # configuration of main proxy # ----------- global configuration parameters ------------------------ debug=4 # debug level (cmd line: -dddddddddd) fork=yes #log_stderror=yes # (cmd line: -E) /* Uncomment these lines to enter debugging mode fork=no log_stderror=yes */ check_via=no # (cmd. line: -v) dns=no # (cmd. line: -r) rev_dns=no # (cmd. line: -R) children=4 fifo="/tmp/openserbp_fifo" # syslog logging, configure in /etc/syslog log_facility=LOG_LOCAL1 # /var/log/openserbp.log server_header ="server_header: itsp1, border proxy" user_agent_header="user_agent_header: itsp1, border proxy" listen=udp:10.10.0.41:5062 listen=udp:10.10.0.46:5062 listen=tcp:10.10.0.41:5062 listen=tcp:10.10.0.41:5080 listen=tls:10.10.0.41:5063 listen=tls:10.10.0.41:5065 alias=itsp1.ienum.labs.nic.at # # uncomment the following lines for TLS support # # default fed = fedA disable_tls = 0 tls_verify_server = 1 tls_verify_client = 1 tls_require_client_certificate = 1 tls_method = TLSv1 tls_certificate = "/etc/certs/fedA/itsp1/cert.pem" tls_private_key = "/etc/certs/fedA/itsp1/privkey.pem" tls_ca_list = "/etc/certs/fedA/demoCA/cacert.pem" #tls_ciphers_list= "NULL" #tls core supports only integer client domain AVPs tls_client_domain_avp = 400 tls_client_domain["v2"] { #specify parameters for a domain in particular, otherwise, #it will use the default. These are the possible parameters to #change for each domain tls_certificate = "/etc/certs/fedA/itsp1/cert.pem" tls_private_key = "/etc/certs/fedA/itsp1/privkey.pem" tls_ca_list = "/etc/certs/fedA/demoCA/cacert.pem" tls_method=TLSv1 # tls_ciphers_list= "NULL" tls_verify_server = 1 } tls_client_domain["fedc"] { #specify parameters for a domain in particular, otherwise, #it will use the default. These are the possible parameters to #change for each domain tls_certificate = "/etc/certs/fedC/itsp1/cert.pem" tls_private_key = "/etc/certs/fedC/itsp1/privkey.pem" tls_ca_list = "/etc/certs/fedC/demoCA/cacert.pem" tls_method=TLSv1 tls_verify_server = 1 } tls_server_domain[10.10.0.41:5065] { #specify parameters for a domain in particular, otherwise, #it will use the default. These are the possible parameters to #change for each domain tls_certificate = "/etc/certs/fedA/itsp1/cert.pem" tls_private_key = "/etc/certs/fedA/itsp1/privkey.pem" tls_ca_list = "/etc/certs/fedA/demoCA/cacert.pem" tls_method=TLSv1 tls_verify_client = 1 tls_require_client_certificate = 1 } tls_server_domain[10.10.0.41:5067] { #specify parameters for a domain in particular, otherwise, #it will use the default. These are the possible parameters to #change for each domain tls_certificate = "/etc/certs/fedC/itsp1/cert.pem" tls_private_key = "/etc/certs/fedC/itsp1/privkey.pem" tls_ca_list = "/etc/certs/fedC/demoCA/cacert.pem" tls_method=TLSv1 tls_verify_client = 0 tls_require_client_certificate = 1 } # ------------------ module loading ---------------------------------- # Uncomment this if you want to use SQL database loadmodule "/usr/lib/openser/modules/mysql.so" loadmodule "/usr/lib/openser/modules/sl.so" loadmodule "/usr/lib/openser/modules/tm.so" loadmodule "/usr/lib/openser/modules/rr.so" loadmodule "/usr/lib/openser/modules/maxfwd.so" loadmodule "/usr/lib/openser/modules/textops.so" loadmodule "/usr/lib/openser/modules/xlog.so" loadmodule "/usr/lib/openser/modules/avpops.so" loadmodule "/usr/lib/openser/modules/options.so" loadmodule "/usr/lib/openser/modules/acc.so" loadmodule "/usr/lib/openser/modules/uri.so" loadmodule "/usr/lib/openser/modules/alias_db.so" loadmodule "/usr/lib/openser/modules/enum.so" loadmodule "/usr/lib/openser/modules/domain.so" loadmodule "/usr/lib/openser/modules/domainpolicy.so" loadmodule "/usr/lib/openser/modules/tlsops.so" # ----------------- setting module-specific parameters --------------- # -- database configuration -- # read only DB access # modparam("domain", "db_url", "postgres://openserro:openserro@localhost/openser") # XXX user at43 db for production use modparam("domain|uri_db|alias_db|lcr", "db_url", "mysql://openserro:openserro@127.0.0.1/openserbp") #modparam("avpops", "avp_url", "postgres://serro:rororo@83.136.32.160/at43") # read and write DB access modparam("acc" , "db_url", "mysql://openser:openserrw@127.0.0.1/openserbp") # the following modules also supports multidomain, but are not used # by us: group, group_radius, speeddial, uri_db modparam("alias_db|usrloc|registrar|avpops", "use_domain", 1) modparam("domain", "db_mode", 1) # Use caching in domain module # accounting parameters modparam("acc", "early_media", 1) modparam("acc", "report_ack", 1) modparam("acc", "report_cancels", 0) modparam("acc", "multi_leg_enabled", 0) modparam("acc", "failed_transaction_flag", 4) # radius, syslog and DB modparam("acc", "log_flag", 1) # syslog modparam("acc", "db_flag", 2) # DB modparam("acc", "db_missed_flag", 3) # DB missed calles table #modparam("acc", "swap_direction", 0) #modparam("acc", "detect_direction", 1) # extra accounting of TLS paramters and sockets modparam("acc", "db_extra", "source_ip=$si;source_port=$sp;received_ip=$Ri;received_port=$Rp;tls_peer_subject=$tls_peer_subject;tls_peer_issuer=$tls_peer_issuer;tls_my_subject=$tls_my_subject;tls_my_issuer=$tls_my_issuer") # carrier enum modparam("enum", "branchlabel", "i") modparam("enum", "bl_algorithm", "cc") # we do never read from the default AVP table, thus we define a dummy # table which will never be used #modparam("avpops", "avp_table", "dummy") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) modparam("domainpolicy", "port_override_avp", "portoverride") modparam("domainpolicy", "transport_override_avp", "transportoverride") modparam("domainpolicy", "domain_prefix_avp", "domainprefix") modparam("domainpolicy", "domain_suffix_avp", "domainsuffix") modparam("domainpolicy", "send_socket_avp", "sendsocket") # ------------------------- request routing logic ------------------- # main routing logic route{ remove_hf("Supported"); route(21); # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; setflag(1); setflag(2); setflag(3); setflag(4); # we do not allow REGISTER messages, they are # not needed for VoIP peering if (is_method("REGISTER")) { xlog("L_INFO","[$Tf] [$si:$sp] $rm $ru (From: $fu -> To: $tu) reject with 405\n"); sl_send_reply("405","REGISTER is not allowed"); exit; } # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { if (!has_totag()) { xlog("L_WARN","loose_route request without to-tag, 403...\n"); sl_send_reply("403", "out-of-dialog loose_route not allowed"); exit; } xlog("L_WARN","request loose_route processed...\n"); route(1); }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol record_route(); if ( is_method("ACK") ) { if ( t_check_trans() ) { # non loose-route, but stateful ACK; must be an ACK after a 487 xlog("L_INFO","local end-to-end ACK for an existent INVITE transaction detected ...t_relay()\n"); t_relay(); } else { xlog("L_WARN","ACK without matching transaction ... ignore and discard.\n"); } exit; } if ( has_totag() ) { # in-dialog requests should be handled by loose_route # this should be fixed in the client or in openser xlog("L_ERR","in-dialog request was not catched by loose_route block, 403... \n"); xlog("L_ERR","this is the complete message:\n"); xlog("L_ERR","$mb\n"); sl_send_reply("403","in-dialog request without loose_route is not allowed, this is a bug in the client or in this proxy\n"); exit; } # # We only allow requests which are directly addressed to us. # This is a security feature to avoid bypassing of the # main proxy routing logic if (!uri==myself) { xlog("L_INFO","[$Tf] [$si:$sp] $rm $ru (From: $fu -> To: $tu) non local domain, reject with 403\n"); sl_send_reply("403","please address requests directly to me"); exit; }; # allow OPTIONS requests for keep alive monitoring if ( is_method("OPTIONS") && !(uri =~ "sip:.*@.*") ) { # OPTIONS request for us options_reply(); exit; } # # at this time, we only have out-of-dialog requests # now, we split routing depending on the direction # the protocol can be used to identify the direction # tls: request from peering partner # udp,tcp: local request if(proto==tls) { route(2); } else { route(3); }; exit; } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; exit; } route[2] { # TLS requests - thus requests received from peering partners. # This requests are already authenticated via TLS certificate # validation, thus no further SIP based authentication needed. # Just forward the requests to the main proxy. # Beforehand, we map from the anonymous IENUM SIP URIs to the # user's AoR using the dbaliases module. Note: this mapping can # also be done in the main proxy. xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(2): TLS requests\n"); if (alias_db_lookup("dbaliases")) { xlog("L_INFO","dbaliases lookup successful, new URI = $ru\n"); avp_write("s:outbound.itsp1.ienum.labs.nic.at","$avp(i:679)"); avp_pushto("$du","$avp(i:679)"); route(5); exit; } xlog("L_INFO","dbaliases lookup failed ... 404\n"); sl_send_reply("404","Unknown user"); exit; } route[3] { # UDP/TCP - thus requests received from the local main proxy. # This requests are already authenticated by the main proxy, # thus no further SIP based authentication needed. # # Perform the following steps: # 1. Try I-ENUM lookup # 2. Try U-ENUM lookup # 3. reply with 499 xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(3): UDP/TCP requests\n"); if ( i_enum_query("ienum.labs.nic.at") ) { xlog("L_INFO","I-ENUM lookup successful, new URI = $ru, check if useful...\n"); if (uri == myself) { xlog("L_INFO","I-ENUM SIP URI belongs to myself, relay back to main proxy...\n"); sl_send_reply("100","I-ENUM succeeded, number belongs to us, sending back to main proxy..."); route(2); exit; } if (dp_can_connect()) { # check the domainpolicy of the destination xlog("L_INFO","dp_can_connect succeeded:\n"); xlog("L_INFO"," port_override_avp = '$avp(s:portoverride)'\n"); xlog("L_INFO"," transport_override_avp = '$avp(s:transportoverride)'\n"); xlog("L_INFO"," domain_prefix_avp = '$avp(s:domainprefix)'\n"); xlog("L_INFO"," domain_suffix_avp = '$avp(s:domainsuffix)'\n"); xlog("L_INFO"," send_socket_avp = '$avp(s:sendsocket)'\n"); xlog("L_INFO"," tls_client_domain_avp = '$avp(i:400)'\n"); # apply domain policy if (dp_apply_policy()) { xlog("L_INFO","I-ENUM SIP URI is a known peering partner, use this uri and relay request...\n"); xlog("L_INFO"," R-URI = $ru\n"); xlog("L_INFO"," d-URI = $du\n"); sl_send_reply("100","I-ENUM und domainpolicy succeeded, sending to other domain..."); route(4); exit; } xlog("L_INFO","dp_apply_policy failed\n"); } else { xlog("L_INFO","dp_apply_policy failed\n"); } } else { xlog("L_INFO","I-ENUM lookup failed, fallback to U-ENUM ...\n"); } revert_uri(); if ( enum_query("ienum.labs.nic.at") ) { xlog("L_INFO","U-ENUM lookup successful, new URI = $ru, relay request...\n"); sl_send_reply("100","U-ENUM succeeded, sending to other domain..."); route(4); exit; } xlog("L_INFO","I-ENUM lookup failed, send 499 back to main proxy ...\n"); sl_send_reply("499","Number not in ENUM or domainpolicy failed"); exit; } route[4]{ # This route block reformats the rpid from the internal number format # to the number format used in the I-ENUM trial. # Thus, the proxy will take the existing rpid information (Remote-Party-ID header # or P-Asserted_Identity header) and build an P-Asserted-Identity header with # a tel URI. xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(4): add P-Asserted-Identity header\n"); # uncomment the next few lines if the local rpid format is # Remote-Party-ID: ;... # first the dirty hack version: just use a regexp over the whole message # this will fail with unusual formated SIP messages #if (subst('/^Remote-Party-ID:(.*)sip:([^@]*)@[a-zA-Z0-9.]+(.*)$/P-Asserted-Identity: tel:\2\r/ig')) { # xlog("L_INFO","successfuly replaced Remote-Party-ID with /P-Asserted-Identity...\n"); #} else { # xlog("L_INFO","failure replacing Remote-Party-ID with P-Asserted-Identity...500\n"); # sl_send_reply("500","failure replacing Remote-Party-ID with P-Asserted-Identity"); # exit; #} # # now the more complicated version using AVPs, should be more robust: # write the complete content of the Remote-Party-ID header into an AVP append_hf("Remote-Party-ID: $avp(s:rpidheader)\r\n"); # replace the sip uri with a tel uri # \1 \2 \3 \4 avp_subst("$avp(s:rpidheader)", "/^(.*)(.*)/tel:\2/gi"); # remove the Remote-Party-ID header and create the P-Asserted-Identity header remove_hf("Remote-Party-ID"); append_hf("P-Asserted-Identity: $avp(s:rpidheader)\r\n"); route(1); exit; } route[5]{ # This route block reformats the rpid received in the P-Asserted-Identity header # (the number format used in the I-ENUM trial) into the local used format. # The local used format may be for example a Remote-Party-ID header, a # P-Asserted_Identity header or a rewritten From header. xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(5): process P-Asserted-Identity header\n"); route(1); exit; } onreply_route { # default onreply_route route(22); } route[21] { xlog("L_INFO","$ci [$Tf]: -------------------- new SIP request received --------------------\n"); xlog("L_INFO","$ci [$si:$sp] $rm $ru (From: $fu -> To: $tu)\n"); xlog("L_INFO","[$Tf] $$tls_peer_subject = '$tls_peer_subject'\n"); xlog("L_INFO","[$Tf] $$tls_peer_subject_cn = '$tls_peer_subject_cn'\n"); xlog("L_DBG", "message buffer:\n"); xlog("L_DBG", "$mb\n"); } route[22] { xlog("L_INFO","$ci [$Tf]: -------------------- new SIP response received --------------------\n"); xlog("L_INFO","$ci [$si:$sp] $rs ($cs) $rr (From: $fu <- To: $tu)\n"); xlog("L_INFO","[$Tf] $$tls_peer_subject = '$tls_peer_subject'\n"); xlog("L_INFO","[$Tf] $$tls_peer_subject_cn = '$tls_peer_subject_cn'\n"); xlog("L_DBG", "message buffer:\n"); xlog("L_DBG", "$mb\n"); }