From 74926f7a13ab8772d94f6e9304fb9218cbfc8792 Mon Sep 17 00:00:00 2001 From: Stefan Sayer Date: Sat, 8 Jan 2011 16:06:41 +0100 Subject: [PATCH] sbc: adds the refuse_with option if refuse_with is set, all calls in this profile are refused with the specified response code and reason. pattern replacement is enabled on both code and reason. append_headers option, too. --- apps/sbc/SBC.cpp | 80 +++++++++++++++++++++----- apps/sbc/SBC.h | 3 + apps/sbc/SBCCallProfile.cpp | 88 ++++++++++++++++------------- apps/sbc/SBCCallProfile.h | 2 + apps/sbc/etc/refuse.sbcprofile.conf | 8 +++ doc/Readme.sbc.txt | 15 +++++ 6 files changed, 141 insertions(+), 55 deletions(-) create mode 100644 apps/sbc/etc/refuse.sbcprofile.conf diff --git a/apps/sbc/SBC.cpp b/apps/sbc/SBC.cpp index bd578cfb..3d01ae3d 100644 --- a/apps/sbc/SBC.cpp +++ b/apps/sbc/SBC.cpp @@ -117,9 +117,10 @@ AmSession* SBCFactory::onInvite(const AmSipRequest& req) { profiles_mut.lock(); + string app_param = getHeader(req.hdrs, PARAM_HDR, true); + string profile = active_profile; if (profile == "$(paramhdr)") { - string app_param = getHeader(req.hdrs, PARAM_HDR, true); profile = get_header_keyvalue(app_param,"profile"); } else if (profile == "$(ruri.user)") { profile = req.user; @@ -131,19 +132,64 @@ AmSession* SBCFactory::onInvite(const AmSipRequest& req) profiles_mut.unlock(); ERROR("could not find call profile '%s' (active_profile = %s)\n", profile.c_str(), active_profile.c_str()); - throw AmSession::Exception(500,"Server Internal Error"); + throw AmSession::Exception(500,SIP_REPLY_SERVER_INTERNAL_ERROR); } DBG("using call profile '%s'\n", profile.c_str()); SBCCallProfile& call_profile = it->second; + + if (!call_profile.refuse_with.empty()) { + AmUriParser ruri_parser, from_parser, to_parser; +#define REPLACE_VALS req, app_param, ruri_parser, from_parser, to_parser + string refuse_with = replaceParameters(call_profile.refuse_with, + "refuse_with", REPLACE_VALS); + + if (refuse_with.empty()) { + ERROR("refuse_with empty after replacing (was '%s' in profile %s)\n", + call_profile.refuse_with.c_str(), call_profile.profile_file.c_str()); + profiles_mut.unlock(); + throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR); + } + + size_t spos = refuse_with.find(' '); + unsigned int refuse_with_code; + if (spos == string::npos || spos == refuse_with.size() || + str2i(refuse_with.substr(0, spos), refuse_with_code)) { + ERROR("invalid refuse_with '%s'->'%s' in %s. Expected \n", + call_profile.refuse_with.c_str(), refuse_with.c_str(), + call_profile.profile_file.c_str()); + profiles_mut.unlock(); + throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR); + } + string refuse_with_reason = refuse_with.substr(spos+1); + + string hdrs = replaceParameters(call_profile.append_headers, + "append_headers", REPLACE_VALS); + profiles_mut.unlock(); + + if (hdrs.size()>2) + assertEndCRLF(hdrs); + + DBG("refusing call with %u %s\n", refuse_with_code, refuse_with_reason.c_str()); + AmSipDialog::reply_error(req, refuse_with_code, refuse_with_reason, hdrs); + + return NULL; +#undef REPLACE_VALS + } + AmConfigReader& sst_cfg = call_profile.use_global_sst_config ? cfg : call_profile.cfg; // override with profile config if (call_profile.sst_enabled) { DBG("Enabling SIP Session Timers\n"); - if (!session_timer_fact->onInvite(req, sst_cfg)) { + try { + if (!session_timer_fact->onInvite(req, sst_cfg)) { + profiles_mut.unlock(); + return NULL; + } + } catch (const AmSession::Exception& e) { profiles_mut.unlock(); - return NULL; + throw; } } @@ -362,13 +408,13 @@ void SBCDialog::onInvite(const AmSipRequest& req) DBG("processing initial INVITE\n"); - if(dlg.reply(req, 100, "Connecting") != 0) { - throw AmSession::Exception(500,"Failed to reply 100"); - } +#define REPLACE_VALS req, app_param, ruri_parser, from_parser, to_parser string app_param = getHeader(req.hdrs, PARAM_HDR, true); -#define REPLACE_VALS req, app_param,ruri_parser, from_parser, to_parser + if(dlg.reply(req, 100, "Connecting") != 0) { + throw AmSession::Exception(500,"Failed to reply 100"); + } ruri = call_profile.ruri.empty() ? req.r_uri : replaceParameters(call_profile.ruri, "RURI", REPLACE_VALS); @@ -430,13 +476,7 @@ void SBCDialog::onInvite(const AmSipRequest& req) string append_headers = replaceParameters(call_profile.append_headers, "append_headers", REPLACE_VALS); if (append_headers.size()>2) { - if (append_headers[append_headers.size()-2] != '\r' || - append_headers[append_headers.size()-1] != '\n') { - while ((append_headers[append_headers.size()-1] == '\r') || - (append_headers[append_headers.size()-1] == '\n')) - append_headers.erase(append_headers.size()-1); - append_headers += "\r\n"; - } + assertEndCRLF(append_headers); invite_req.hdrs+=append_headers; } } @@ -1009,3 +1049,13 @@ int SBCCalleeSession::filterBody(AmSdp& sdp, bool is_a2b) { } return 0; } + +void assertEndCRLF(string& s) { + if (s[s.size()-2] != '\r' || + s[s.size()-1] != '\n') { + while ((s[s.size()-1] == '\r') || + (s[s.size()-1] == '\n')) + s.erase(s.size()-1); + s += "\r\n"; + } +} diff --git a/apps/sbc/SBC.h b/apps/sbc/SBC.h index d0bada12..f38a1f0f 100644 --- a/apps/sbc/SBC.h +++ b/apps/sbc/SBC.h @@ -165,4 +165,7 @@ class SBCCalleeSession void setAuthHandler(AmSessionEventHandler* h) { auth = h; } }; + +static void assertEndCRLF(string& s); + #endif diff --git a/apps/sbc/SBCCallProfile.cpp b/apps/sbc/SBCCallProfile.cpp index 5f02442f..440a6ad1 100644 --- a/apps/sbc/SBCCallProfile.cpp +++ b/apps/sbc/SBCCallProfile.cpp @@ -170,6 +170,8 @@ bool SBCCallProfile::readFromConfiguration(const string& name, // append_headers=P-Received-IP: $si\r\nP-Received-Port:$sp\r\n append_headers = cfg.getParameter("append_headers"); + refuse_with = cfg.getParameter("refuse_with"); + md5hash = ""; if (!cfg.getMD5(profile_file_name, md5hash)){ ERROR("calculating MD5 of file %s\n", profile_file_name.c_str()); @@ -177,47 +179,51 @@ bool SBCCallProfile::readFromConfiguration(const string& name, INFO("SBC: loaded SBC profile '%s' - MD5: %s\n", name.c_str(), md5hash.c_str()); - INFO("SBC: RURI = '%s'\n", ruri.c_str()); - INFO("SBC: From = '%s'\n", from.c_str()); - INFO("SBC: To = '%s'\n", to.c_str()); - if (!callid.empty()) { - INFO("SBC: Call-ID = '%s'\n", callid.c_str()); - } + if (!refuse_with.empty()) { + INFO("SBC: refusing calls with '%s'\n", refuse_with.c_str()); + } else { + INFO("SBC: RURI = '%s'\n", ruri.c_str()); + INFO("SBC: From = '%s'\n", from.c_str()); + INFO("SBC: To = '%s'\n", to.c_str()); + if (!callid.empty()) { + INFO("SBC: Call-ID = '%s'\n", callid.c_str()); + } - INFO("SBC: force outbound proxy: %s\n", force_outbound_proxy?"yes":"no"); - INFO("SBC: outbound proxy = '%s'\n", outbound_proxy.c_str()); - if (!next_hop_ip.empty()) { - INFO("SBC: next hop = %s%s\n", next_hop_ip.c_str(), - next_hop_port.empty()? "" : (":"+next_hop_port).c_str()); - } + INFO("SBC: force outbound proxy: %s\n", force_outbound_proxy?"yes":"no"); + INFO("SBC: outbound proxy = '%s'\n", outbound_proxy.c_str()); + if (!next_hop_ip.empty()) { + INFO("SBC: next hop = %s%s\n", next_hop_ip.c_str(), + next_hop_port.empty()? "" : (":"+next_hop_port).c_str()); + } - INFO("SBC: header filter is %s, %zd items in list\n", - FilterType2String(headerfilter), headerfilter_list.size()); - INFO("SBC: message filter is %s, %zd items in list\n", - FilterType2String(messagefilter), messagefilter_list.size()); - INFO("SBC: SDP filter is %sabled, %s, %zd items in list\n", - sdpfilter_enabled?"en":"dis", FilterType2String(sdpfilter), - sdpfilter_list.size()); - - INFO("SBC: SST %sabled\n", sst_enabled?"en":"dis"); - INFO("SBC: SIP auth %sabled\n", auth_enabled?"en":"dis"); - INFO("SBC: call timer %sabled\n", call_timer_enabled?"en":"dis"); - if (call_timer_enabled) { - INFO("SBC: %s seconds\n", call_timer.c_str()); - } - INFO("SBC: prepaid %sabled\n", prepaid_enabled?"en":"dis"); - if (prepaid_enabled) { - INFO("SBC: acc_module = '%s'\n", prepaid_accmodule.c_str()); - INFO("SBC: uuid = '%s'\n", prepaid_uuid.c_str()); - INFO("SBC: acc_dest = '%s'\n", prepaid_acc_dest.c_str()); - } - if (reply_translations.size()) { - string reply_trans_codes; - for(map >::iterator it= - reply_translations.begin(); it != reply_translations.end(); it++) - reply_trans_codes += int2str(it->first)+", "; - reply_trans_codes.erase(reply_trans_codes.length()-2); - INFO("SBC: reply translation for %s\n", reply_trans_codes.c_str()); + INFO("SBC: header filter is %s, %zd items in list\n", + FilterType2String(headerfilter), headerfilter_list.size()); + INFO("SBC: message filter is %s, %zd items in list\n", + FilterType2String(messagefilter), messagefilter_list.size()); + INFO("SBC: SDP filter is %sabled, %s, %zd items in list\n", + sdpfilter_enabled?"en":"dis", FilterType2String(sdpfilter), + sdpfilter_list.size()); + + INFO("SBC: SST %sabled\n", sst_enabled?"en":"dis"); + INFO("SBC: SIP auth %sabled\n", auth_enabled?"en":"dis"); + INFO("SBC: call timer %sabled\n", call_timer_enabled?"en":"dis"); + if (call_timer_enabled) { + INFO("SBC: %s seconds\n", call_timer.c_str()); + } + INFO("SBC: prepaid %sabled\n", prepaid_enabled?"en":"dis"); + if (prepaid_enabled) { + INFO("SBC: acc_module = '%s'\n", prepaid_accmodule.c_str()); + INFO("SBC: uuid = '%s'\n", prepaid_uuid.c_str()); + INFO("SBC: acc_dest = '%s'\n", prepaid_acc_dest.c_str()); + } + if (reply_translations.size()) { + string reply_trans_codes; + for(map >::iterator it= + reply_translations.begin(); it != reply_translations.end(); it++) + reply_trans_codes += int2str(it->first)+", "; + reply_trans_codes.erase(reply_trans_codes.length()-2); + INFO("SBC: reply translation for %s\n", reply_trans_codes.c_str()); + } } if (append_headers.size()) { @@ -248,7 +254,9 @@ bool SBCCallProfile::operator==(const SBCCallProfile& rhs) const { auth_enabled == rhs.auth_enabled && call_timer_enabled == rhs.call_timer_enabled && prepaid_enabled == rhs.prepaid_enabled && - reply_translations == reply_translations; + reply_translations == rhs.reply_translations && + append_headers == rhs.append_headers && + refuse_with == rhs.refuse_with; if (sdpfilter_enabled) { res = res && diff --git a/apps/sbc/SBCCallProfile.h b/apps/sbc/SBCCallProfile.h index 353fa73b..d9511c4a 100644 --- a/apps/sbc/SBCCallProfile.h +++ b/apps/sbc/SBCCallProfile.h @@ -85,6 +85,8 @@ struct SBCCallProfile { string append_headers; + string refuse_with; + // todo: RTP forwarding mode // todo: RTP transcoding mode diff --git a/apps/sbc/etc/refuse.sbcprofile.conf b/apps/sbc/etc/refuse.sbcprofile.conf new file mode 100644 index 00000000..8545c390 --- /dev/null +++ b/apps/sbc/etc/refuse.sbcprofile.conf @@ -0,0 +1,8 @@ +# refuse SBC profile +# +# This refuses calls with "403 Forbidden" and adds a +# Warning header with "Incoming calls not allowed". +# +# note: warning code 393 might not be the proper one for this usage. +refuse_with="403 Forbidden" +append_headers="Warning: 393 $rd \"Incoming calls not allowed\"" \ No newline at end of file diff --git a/doc/Readme.sbc.txt b/doc/Readme.sbc.txt index f63ef4f1..bbd4a012 100644 --- a/doc/Readme.sbc.txt +++ b/doc/Readme.sbc.txt @@ -17,6 +17,7 @@ Features o online reload of call profiles o From, To, RURI, Call-ID update o Header and message filter + o adding arbitrary headers o reply code translation o SIP authentication o SIP Session Timers @@ -302,6 +303,19 @@ points for integration into custom billing systems. Parallel call limits can be implemented by implementing an account specific limit to the accounting module. +Refusing calls +-------------- + +In some configurations, if may be necessary to refuse calls with a certain error response +code and reason. If the refuse_with call profile option is set, the call is refused with +the code and reason specified. In this case, all other call profile options are ignored, +only the append_headers option has effect. + +Examples: + refuse_with="403 Invalid Domain $rd" + + refuse_with="606 Not Acceptable" + append_headers="P-Original-URI: $r\r\nP-Original-To: $t" Example profiles ---------------- @@ -312,6 +326,7 @@ Example profiles prepaid - prepaid accounting (obsoletes sw_prepaid_sip app) codecfilter - let only some low bitrate codecs pass replytranslate - swap 603 and 488 response code in replies + refuse - refuse all calls with 403 Forbidden Dependencies ------------