/* * Copyright (C) 2011 Stefan Sayer * * This file is part of SEMS, a free SIP media server. * * SEMS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * For a license to use the SEMS software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * SEMS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "AmPlugIn.h" #include "log.h" #include "AmArg.h" #include "BLRedis.h" #include "SBCCallControlAPI.h" #include "AmSipHeaders.h" #include class CCBLRedisFactory : public AmDynInvokeFactory { public: CCBLRedisFactory(const string& name) : AmDynInvokeFactory(name) {} AmDynInvoke* getInstance(){ return CCBLRedis::instance(); } int onLoad(){ if (CCBLRedis::instance()->onLoad()) return -1; DBG("REDIS blacklist call control loaded.\n"); return 0; } }; EXPORT_PLUGIN_CLASS_FACTORY(CCBLRedisFactory, MOD_NAME); CCBLRedis* CCBLRedis::_instance=0; CCBLRedis* CCBLRedis::instance() { if(!_instance) _instance = new CCBLRedis(); return _instance; } CCBLRedis::CCBLRedis() { } CCBLRedis::~CCBLRedis() { } int CCBLRedis::onLoad() { AmConfigReader cfg; string redis_server = "127.0.0.1"; string redis_port = "6379"; string redis_reconnect_timers = "5,10,20,50,100,500,1000"; string redis_connections = "10"; string redis_max_conn_wait = "1000"; pass_on_bl_unavailable = false; full_logging = false; if(cfg.loadPluginConf(AmConfig::ModConfigPath, MOD_NAME)) { INFO(MOD_NAME "configuration file not found, assuming default " "configuration is fine\n"); } else { redis_server = cfg.getParameter("redis_server", redis_server); redis_port = cfg.getParameter("redis_port", redis_port); redis_reconnect_timers = cfg.getParameter("redis_reconnect_timers", redis_reconnect_timers); redis_connections = cfg.getParameter("redis_connections", redis_connections); redis_max_conn_wait = cfg.getParameter("redis_max_conn_wait", redis_max_conn_wait); full_logging = cfg.getParameter("redis_full_logging", "no")=="yes"; pass_on_bl_unavailable = cfg.getParameter("pass_on_bl_unavailable", "no")=="yes"; } unsigned int i_redis_connections; if (str2int(redis_connections, i_redis_connections)) { ERROR("could not understand redis_connections=%s\n", redis_connections.c_str()); return -1; } unsigned int i_redis_port; if (str2int(redis_port, i_redis_port)) { ERROR("could not understand redis_port=%s\n", redis_port.c_str()); return -1; } unsigned int i_redis_max_conn_wait; if (str2int(redis_max_conn_wait, i_redis_max_conn_wait)) { ERROR("could not understand redis_max_conn_wait=%s\n", redis_max_conn_wait.c_str()); return -1; } std::vector reconnect_timers; std::vector timeouts_v = explode(redis_reconnect_timers, ","); for (std::vector::iterator it= timeouts_v.begin(); it != timeouts_v.end(); it++) { int r; if (!str2int(*it, r)) { ERROR("REDIS reconnect timeout '%s' not understood\n", it->c_str()); return -1; } reconnect_timers.push_back(r); } connection_pool.set_config(redis_server, i_redis_port, reconnect_timers, i_redis_max_conn_wait); connection_pool.add_connections(i_redis_connections); connection_pool.start(); DBG("setting max number max_retries to %u (as connections)\n", i_redis_connections); max_retries = i_redis_connections; return 0; } void CCBLRedis::invoke(const string& method, const AmArg& args, AmArg& ret) { DBG("CCBLRedis: %s(%s)\n", method.c_str(), AmArg::print(args).c_str()); if(method == "start"){ SBCCallProfile* call_profile = dynamic_cast(args[CC_API_PARAMS_CALL_PROFILE].asObject()); start(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(), args[CC_API_PARAMS_LTAG].asCStr(), call_profile, args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_START_SEC].asInt(), args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_START_USEC].asInt(), args[CC_API_PARAMS_CFGVALUES], args[CC_API_PARAMS_TIMERID].asInt(), ret); } else if(method == "connect"){ // SBCCallProfile* call_profile = // dynamic_cast(args[CC_API_PARAMS_CALL_PROFILE].asObject()); // connect(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(), // args[CC_API_PARAMS_LTAG].asCStr(), // call_profile, // args[CC_API_PARAMS_OTHERID].asCStr(), // args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_CONNECT_SEC].asInt(), // args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_CONNECT_USEC].asInt()); } else if(method == "end"){ // SBCCallProfile* call_profile = // dynamic_cast(args[CC_API_PARAMS_CALL_PROFILE].asObject()); // end(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(), // args[CC_API_PARAMS_LTAG].asCStr(), // call_profile, // args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_END_SEC].asInt(), // args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_END_USEC].asInt() // ); } else if(method == "_list"){ ret.push("start"); ret.push("connect"); ret.push("end"); } else throw AmDynInvoke::NotImplemented(method); } int CCBLRedis::handle_redis_reply(redisContext* redis_context, redisReply* reply, bool& hit) { hit = false; if (!reply) { switch (redis_context->err) { case REDIS_ERR_IO: ERROR("I/O error: %s\n", strerror(errno)); return RWT_E_CONNECTION; case REDIS_ERR_EOF: // silently reconnect return RWT_E_CONNECTION; case REDIS_ERR_PROTOCOL: ERROR("REDIS Protocol error detected\n"); return RWT_E_CONNECTION; case REDIS_ERR_OTHER: return RWT_E_CONNECTION; } } switch (reply->type) { case REDIS_REPLY_ERROR: ERROR("REDIS ERROR: %s\n", reply->str); return RWT_E_WRITE; case REDIS_REPLY_STATUS: case REDIS_REPLY_STRING: if (reply->len>=0) { if (full_logging) { DBG("REDIS: %.*s\n", reply->len, reply->str); } hit = true; } break; case REDIS_REPLY_INTEGER: if (full_logging) { DBG("REDIS: %lld\n", reply->integer); } // TODO: add other return codes/cmd if (reply->integer) { hit = true; } break; case REDIS_REPLY_ARRAY: { for (size_t i=0;ielements;i++) { switch(reply->element[i]->type) { case REDIS_REPLY_ERROR: ERROR("REDIS ERROR: %.*s\n", reply->element[i]->len, reply->element[i]->str); return RWT_E_WRITE; case REDIS_REPLY_INTEGER: if (full_logging) { DBG("REDIS: %lld\n", reply->element[i]->integer); } if (reply->element[i]->integer) { hit = true; } break; case REDIS_REPLY_NIL: if (full_logging) { DBG("REDIS: nil\n"); } break; case REDIS_REPLY_STATUS: case REDIS_REPLY_STRING: if (full_logging) { if (reply->element[i]->len >= 0) { DBG("REDIS: %.*s\n", reply->element[i]->len, reply->element[i]->str); } } if (reply->element[i]->len >= 0) { hit = true; } break; default: ERROR("unknown REDIS reply %d!",reply->element[i]->type); break; } } }; break; default: ERROR("unknown REDIS reply %d!", reply->type); break; } return RWT_E_OK; } void CCBLRedis::start(const string& cc_name, const string& ltag, SBCCallProfile* call_profile, int start_ts_sec, int start_ts_usec, const AmArg& values, int timer_id, AmArg& res) { // start code here res.push(AmArg()); AmArg& res_cmd = res[0]; #define MAX_ARGV_ITEMS 20 const char* argv[MAX_ARGV_ITEMS]; size_t argvlen[MAX_ARGV_ITEMS]; unsigned int argv_max = 0; if (!values.hasMember("argc") || str2int(values["argc"].asCStr(), argv_max) || (!argv_max)) { ERROR("deciphering argc\n"); res_cmd[SBC_CC_ACTION] = SBC_CC_REFUSE_ACTION; res_cmd[SBC_CC_REFUSE_CODE] = 500; res_cmd[SBC_CC_REFUSE_REASON] = SIP_REPLY_SERVER_INTERNAL_ERROR; return; } unsigned int argv_index=0; string query; for (; argv_index