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.
sayer/1.4-spce2.6
Stefan Sayer 16 years ago
parent 64bdacd9fc
commit 74926f7a13

@ -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 <code> <reason>\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";
}
}

@ -165,4 +165,7 @@ class SBCCalleeSession
void setAuthHandler(AmSessionEventHandler* h) { auth = h; }
};
static void assertEndCRLF(string& s);
#endif

@ -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 = "<unknown>";
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<unsigned int, std::pair<unsigned int, string> >::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<unsigned int, std::pair<unsigned int, string> >::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 &&

@ -85,6 +85,8 @@ struct SBCCallProfile {
string append_headers;
string refuse_with;
// todo: RTP forwarding mode
// todo: RTP transcoding mode

@ -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\""

@ -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
------------

Loading…
Cancel
Save