void HTTPCallback::worker_thread_entry_point() { CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); Timer* timer; while (_q.pop(timer)) { // Set up the request details. curl_easy_setopt(curl, CURLOPT_URL, timer->callback_url.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, timer->callback_body.data()); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, timer->callback_body.length()); // Include the sequence number header. struct curl_slist* headers = NULL; headers = curl_slist_append(headers, (std::string("X-Sequence-Number: ") + std::to_string(timer->sequence_number)).c_str()); headers = curl_slist_append(headers, "Content-Type: application/octet-stream"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Send the request CURLcode curl_rc = curl_easy_perform(curl); if (curl_rc == CURLE_OK) { _handler->return_timer(timer, true); timer = NULL; // We relinquish control of the timer when we give it back to the store. } else { if (curl_rc == CURLE_HTTP_RETURNED_ERROR) { long http_rc = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_rc); TRC_WARNING("Got HTTP error %d from %s", http_rc, timer->callback_url.c_str()); } TRC_WARNING("Failed to process callback for %lu: URL %s, curl error was: %s", timer->id, timer->callback_url.c_str(), curl_easy_strerror(curl_rc)); _handler->return_timer(timer, false); timer = NULL; // We relinquish control of the timer when we give it back to the store. } // Tidy up request-speciifc objects curl_slist_free_all(headers); headers = NULL; } // Tidy up thread-specific objects curl_easy_cleanup(curl); return; }
// Returns an AppServerTsx if the load monitor admits the request, and if // the request is either an INVITE or a BYE. AppServerTsx* MementoAppServer::get_app_tsx(AppServerTsxHelper* helper, pjsip_msg* req) { if ((req->line.req.method.id != PJSIP_INVITE_METHOD) && (req->line.req.method.id != PJSIP_BYE_METHOD)) { // Request isn't an INVITE or BYE, no processing is required. return NULL; } // Check for available tokens on the initial request if ((req->line.req.method.id == PJSIP_INVITE_METHOD) && (!_load_monitor->admit_request())) { // LCOV_EXCL_START TRC_WARNING("No available tokens - no memento processing of request"); SAS::Event event(helper->trail(), SASEvent::CALL_LIST_OVERLOAD, 0); SAS::report_event(event); _stat_calls_not_recorded_due_to_overload.increment(); return NULL; // LCOV_EXCL_STOP } TRC_DEBUG("Getting a MementoAppServerTsx"); MementoAppServerTsx* memento_tsx = new MementoAppServerTsx(helper, _call_list_store_processor, _service_name, _home_domain); return memento_tsx; }
void DNSEnumService::parse_naptr_reply(const struct ares_naptr_reply* naptr_reply, std::vector<DNSEnumService::Rule>& rules) { for (const struct ares_naptr_reply* record = naptr_reply; record != NULL; record = record->next) { TRC_DEBUG("Got NAPTR record: %u %u \"%s\" \"%s\" \"%s\" %s", record->order, record->preference, record->service, record->flags, record->regexp, record->replacement); if ((strcasecmp((char*)record->service, "e2u+sip") == 0) || (strcasecmp((char*)record->service, "e2u+pstn:sip") == 0) || (strcasecmp((char*)record->service, "e2u+pstn:tel") == 0)) { boost::regex regex; std::string replace; bool terminal = false; if (!EnumService::parse_regex_replace(std::string((char*)record->regexp), regex, replace)) { TRC_WARNING("DNS ENUM record contains unparseable regular expression: %s", record->regexp); // As above, we don't give up totally here. continue; } // The only valid flag is u. If we see any other flags, we must ignore // the whole record (according to RFC 3761, 2.4.1). if (strcasecmp((char*)record->flags, "u") == 0) { terminal = true; } else if (strcmp((char*)record->flags, "") != 0) { TRC_WARNING("DNS ENUM record contains unknown flags: %s", record->flags); // Note that we don't give up totally here. If we end up with an empty // list, we'll break out then. Otherwise, we'll just try and push on. continue; } rules.push_back(Rule(regex, replace, terminal, record->order, record->preference)); } } std::sort(rules.begin(), rules.end(), DNSEnumService::Rule::compare_order_preference); }
// Parses the given User-Data XML to retrieve a list of all the public IDs. std::vector<std::string> get_public_ids(const std::string& user_data) { std::vector<std::string> public_ids; // Parse the XML document, saving off the passed-in string first (as parsing // is destructive). rapidxml::xml_document<> doc; // This doesn't need freeing - doc is on the stack, and this // uses its memory pool. char* user_data_str = doc.allocate_string(user_data.c_str()); try { doc.parse<rapidxml::parse_strip_xml_namespaces>(user_data_str); } catch (rapidxml::parse_error err) { TRC_DEBUG("Parse error in IMS Subscription document: %s\n\n%s", err.what(), user_data.c_str()); doc.clear(); } // Walk through all nodes in the hierarchy IMSSubscription->ServiceProfile->PublicIdentity // ->Identity. rapidxml::xml_node<>* is = doc.first_node("IMSSubscription"); if (is) { for (rapidxml::xml_node<>* sp = is->first_node("ServiceProfile"); sp; sp = sp->next_sibling("ServiceProfile")) { for (rapidxml::xml_node<>* pi = sp->first_node("PublicIdentity"); pi; pi = pi->next_sibling("PublicIdentity")) { rapidxml::xml_node<>* id = pi->first_node("Identity"); if (id) { public_ids.push_back((std::string)id->value()); } else { TRC_WARNING("PublicIdentity node was missing Identity child: %s", user_data.c_str()); } } } } if (public_ids.size() == 0) { TRC_ERROR("Failed to extract any ServiceProfile/PublicIdentity/Identity nodes from %s", user_data.c_str()); } return public_ids; }
std::string JSONEnumService::lookup_uri_from_user(const std::string &user, SAS::TrailId trail) const { std::string uri; TRC_DEBUG("Translating URI via JSON ENUM lookup"); if (user.empty()) { TRC_INFO("No dial string supplied, so don't do ENUM lookup"); return std::string(); } std::string aus = user_to_aus(user); // Take a read lock on the mutex in RAII style boost::shared_lock<boost::shared_mutex> read_lock(_number_prefixes_rw_lock); const struct NumberPrefix* pfix = prefix_match(aus); if (pfix == NULL) { TRC_WARNING("No matching number range %s from ENUM lookup", user.c_str()); SAS::Event event(trail, SASEvent::ENUM_INCOMPLETE, 0); event.add_var_param(user); SAS::report_event(event); return uri; } // Apply the regular expression to the user string to generate a new // URI. try { uri = boost::regex_replace(aus, pfix->match, pfix->replace); } catch(...) // LCOV_EXCL_START Only throws if expression too complex or similar hard-to-hit conditions { TRC_ERROR("Failed to translate number with regex"); SAS::Event event(trail, SASEvent::ENUM_INCOMPLETE, 1); event.add_var_param(user); SAS::report_event(event); return uri; // LCOV_EXCL_STOP } TRC_INFO("Number %s found, translated URI = %s", user.c_str(), uri.c_str()); SAS::Event event(trail, SASEvent::ENUM_COMPLETE, 0); event.add_var_param(user); event.add_var_param(uri); SAS::report_event(event); return uri; }
void DeleteOldCallFragments::unhandled_exception(CassandraStore::ResultCode status, std::string& description, SAS::TrailId trail) { CassandraStore::Operation::unhandled_exception(status, description, trail); TRC_WARNING("Failed to delete old call list fragments for IMPU %s because '%s' (RC = %d)", _impu.c_str(), description.c_str(), status); sas_log_cassandra_failure(trail, SASEvent::CALL_LIST_TRIM_FAILED, status, description); }
rapidxml::xml_document<>* HSSConnection::parse_xml(std::string raw_data, const std::string& url = "") { rapidxml::xml_document<>* root = new rapidxml::xml_document<>; try { root->parse<0>(root->allocate_string(raw_data.c_str())); } catch (rapidxml::parse_error& err) { // report to the user the failure and their locations in the document. TRC_WARNING("Failed to parse Homestead response:\n %s\n %s\n %s\n", url.c_str(), raw_data.c_str(), err.what()); delete root; root = NULL; } return root; }
// Set up the SNMP agent. Returns 0 if it succeeds. int snmp_setup(const char* name) { // Make sure we start as a subagent, not a master agent. netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); // Use callback-based logging, and integrate it with the Clearwater logger snmp_enable_calllog(); snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, logging_callback, NULL); netsnmp_container_init_list(); int rc = init_agent(name); if (rc != 0) { TRC_WARNING("SNMP AgentX initialization failed"); } else { TRC_STATUS("AgentX agent initialised"); } return rc; }
void SIFCService::get_ifcs_from_id(std::multimap<int32_t, Ifc>& ifc_map, const std::set<int32_t>& ids, std::shared_ptr<xml_document<> > ifc_doc, SAS::TrailId trail) const { // Take a read lock on the mutex in RAII style boost::shared_lock<boost::shared_mutex> read_lock(_sets_rw_lock); for (int id : ids) { TRC_DEBUG("Getting the shared iFCs for ID %d", id); std::map<int, std::vector<std::pair<int32_t, std::string>>>::const_iterator i = _shared_ifc_sets.find(id); if (i != _shared_ifc_sets.end()) { TRC_DEBUG("Found iFC set for ID %d", id); for (std::pair<int32_t, std::string> ifc : i->second) { ifc_map.insert(std::make_pair(ifc.first, Ifc(ifc.second, ifc_doc.get()))); } } else { TRC_WARNING("No iFCs stored for ID %d", id); if (_no_shared_ifcs_set_tbl) { _no_shared_ifcs_set_tbl->increment(); } SAS::Event event(trail, SASEvent::SIFC_NO_SET_FOR_ID, 0); event.add_static_param(id); SAS::report_event(event); } } }
void BGCFSproutletTsx::on_rx_initial_request(pjsip_msg* req) { // Create an ACR for this transaction. _acr = _bgcf->get_acr(trail()); _acr->rx_request(req); std::vector<std::string> bgcf_routes; std::string routing_value; bool routing_with_number = false; PJUtils::update_request_uri_np_data(req, get_pool(req), _bgcf->_enum_service, _bgcf->_override_npdi, trail()); pjsip_uri* req_uri = (pjsip_uri*)req->line.req.uri; URIClass uri_class = URIClassifier::classify_uri(req_uri); if (PJUtils::get_rn(req_uri, routing_value)) { // Find the downstream routes based on the number. bgcf_routes = _bgcf->get_route_from_number(routing_value, trail()); // If there are no matching routes, just route based on the domain - this // only matches any wild card routing set up if (bgcf_routes.empty()) { routing_value = ""; bgcf_routes = _bgcf->get_route_from_domain(routing_value, trail()); } else { routing_with_number = true; } } else if ((uri_class == LOCAL_PHONE_NUMBER) || (uri_class == GLOBAL_PHONE_NUMBER)) { // Try to route based on the phone number first pj_str_t pj_user = PJUtils::user_from_uri(req_uri); routing_value = PJUtils::pj_str_to_string(&pj_user); bgcf_routes = _bgcf->get_route_from_number(routing_value, trail()); // If there are no matching routes, just route based on the domain - this // only matches any wild card routing set up if (bgcf_routes.empty()) { routing_value = ""; bgcf_routes = _bgcf->get_route_from_domain(routing_value, trail()); } else { routing_with_number = true; } } else { routing_value = PJUtils::pj_str_to_string(&((pjsip_sip_uri*)req_uri)->host); // Find the downstream routes based on the domain. bgcf_routes = _bgcf->get_route_from_domain(routing_value, trail()); } if (!bgcf_routes.empty()) { // The BGCF should be in control of what routes get added - delete existing // ones first. PJUtils::remove_hdr(req, &STR_ROUTE); for (std::vector<std::string>::iterator ii = bgcf_routes.begin(); ii != bgcf_routes.end(); ++ii) { pjsip_uri* route_uri = PJUtils::uri_from_string(*ii, get_pool(req), PJ_TRUE); route_uri = (route_uri == NULL) ? route_uri : (pjsip_uri*)pjsip_uri_get_uri(route_uri); if (route_uri != NULL && PJSIP_URI_SCHEME_IS_SIP(route_uri)) { PJUtils::add_route_header(req, (pjsip_sip_uri*)route_uri, get_pool(req)); } else { TRC_WARNING("Configured route (%s) isn't a valid SIP URI", (*ii).c_str()); pjsip_msg* rsp = create_response(req, PJSIP_SC_INTERNAL_SERVER_ERROR); send_response(rsp); free_msg(req); return; } } send_request(req); } else { TRC_DEBUG("No route configured for %s", routing_value.c_str()); if ((routing_value == "") || (routing_with_number)) { // If the routing_value is blank we were trying to route a telephone number and // there are no more routes to try. If we had an rn value and this failed then // there are also no more routes to try. pjsip_msg* rsp = create_response(req, PJSIP_SC_NOT_FOUND, "No route to target"); send_response(rsp); free_msg(req); } else { // Previous behaviour on no route was to try to forward the request as-is, // (so trying to route to the domain in the request URI directly). send_request(req); } } }
void AsCommunicationTracker::check_for_healthy_app_servers() { uint64_t now = current_time_ms(); TRC_DEBUG("Current time is %ld, next AS check at %ld", now, _next_check_time_ms.load()); if (now > _next_check_time_ms) { pthread_mutex_lock(&_lock); if (now > _next_check_time_ms) { TRC_DEBUG("Check for ASs that have become healthy again"); // Don't check again for a while. _next_check_time_ms = current_time_ms() + NEXT_CHECK_INTERVAL_MS; // Iterate through all the AS in our map. If any of them have not had // any failures in the last time period we will log they are now working // correctly and remove them from the map. // // We mutate the map as we iterate over it. The non-standard loop // construct avoids iterator invalidation. std::map<std::string, int>::iterator curr_as = _as_failures.begin(); std::map<std::string, int>::iterator next_as; // We build this string for logging which ASs are in failure. std::string failed_as_string; while (curr_as != _as_failures.end()) { next_as = std::next(curr_as); if (curr_as->second == 0) { TRC_DEBUG("AS %s has become healthy", curr_as->first.c_str()); _as_ok_log->log(curr_as->first.c_str()); _as_failures.erase(curr_as); } else { if (failed_as_string != "") { failed_as_string += ", "; } failed_as_string += curr_as->first; curr_as->second = 0; } curr_as = next_as; } if (_as_failures.empty()) { TRC_DEBUG("All ASs OK - clear the alarm"); // No ASs are currently failed. Clear the alarm. _alarm->clear(); } else { TRC_WARNING("Not clearing the alarm as failures detected for the " "following ASs: %s. Next recheck will be in %ums.", failed_as_string.c_str(), NEXT_CHECK_INTERVAL_MS); } } pthread_mutex_unlock(&_lock); } }
static pj_bool_t process_on_rx_msg(pjsip_rx_data* rdata) { // Do logging. local_log_rx_msg(rdata); sas_log_rx_msg(rdata); SAS::TrailId trail = get_trail(rdata); requests_counter->increment(); // Check whether the request should be processed if (!(load_monitor->admit_request(trail)) && (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) && (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD)) { // Discard non-ACK requests if there are no available tokens. // Respond statelessly with a 503 Service Unavailable, including a // Retry-After header with a zero length timeout. TRC_DEBUG("Rejected request due to overload"); // LCOV_EXCL_START - can't meaningfully verify SAS in UT SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); SAS::Event event(trail, SASEvent::SIP_OVERLOAD, 0); event.add_static_param(load_monitor->get_target_latency()); event.add_static_param(load_monitor->get_current_latency()); event.add_static_param(load_monitor->get_rate_limit()); SAS::report_event(event); SAS::Marker end_marker(trail, MARKER_ID_END, 1u); SAS::report_marker(end_marker); // LCOV_EXCL_STOP pjsip_retry_after_hdr* retry_after = pjsip_retry_after_hdr_create(rdata->tp_info.pool, 0); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_SERVICE_UNAVAILABLE, NULL, (pjsip_hdr*)retry_after, NULL); // We no longer terminate TCP connections on overload as the shutdown has // to wait for existing transactions to end and therefore it takes too // long to get feedback to the downstream node. We expect downstream nodes // to rebalance load if possible triggered by receipt of the 503 responses. overload_counter->increment(); return PJ_TRUE; } // If a message has parse errors, reject it (if it's a request other than ACK) // or drop it (if it's a response or an ACK request). if (!pj_list_empty((pj_list_type*)&rdata->msg_info.parse_err)) { SAS::TrailId trail = get_trail(rdata); TRC_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); pjsip_parser_err_report *err = rdata->msg_info.parse_err.next; while (err != &rdata->msg_info.parse_err) { TRC_VERBOSE("Error parsing header %.*s", (int)err->hname.slen, err->hname.ptr); SAS::Event event(trail, SASEvent::UNPARSEABLE_HEADER, 0); event.add_var_param((int)err->hname.slen, err->hname.ptr); SAS::report_event(event); err = err->next; } if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) { TRC_WARNING("Dropping malformed ACK request"); } else { TRC_WARNING("Rejecting malformed request with a 400 error"); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_REQUEST, NULL, NULL, NULL); } } else { TRC_WARNING("Dropping malformed response"); } // As this message is malformed, return PJ_TRUE to absorb it and // stop later modules from processing it. return PJ_TRUE; } return PJ_FALSE; }
void BgcfService::update_routes() { // Check whether the file exists. struct stat s; TRC_DEBUG("stat(%s) returns %d", _configuration.c_str(), stat(_configuration.c_str(), &s)); if ((stat(_configuration.c_str(), &s) != 0) && (errno == ENOENT)) { TRC_STATUS("No BGCF configuration (file %s does not exist)", _configuration.c_str()); CL_SPROUT_BGCF_FILE_MISSING.log(); return; } TRC_STATUS("Loading BGCF configuration from %s", _configuration.c_str()); // Read from the file std::ifstream fs(_configuration.c_str()); std::string bgcf_str((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>()); if (bgcf_str == "") { // LCOV_EXCL_START TRC_ERROR("Failed to read BGCF configuration data from %s", _configuration.c_str()); CL_SPROUT_BGCF_FILE_EMPTY.log(); return; // LCOV_EXCL_STOP } // Now parse the document rapidjson::Document doc; doc.Parse<0>(bgcf_str.c_str()); if (doc.HasParseError()) { TRC_ERROR("Failed to read BGCF configuration data: %s\nError: %s", bgcf_str.c_str(), rapidjson::GetParseError_En(doc.GetParseError())); CL_SPROUT_BGCF_FILE_INVALID.log(); return; } try { std::map<std::string, std::vector<std::string>> new_domain_routes; std::map<std::string, std::vector<std::string>> new_number_routes; JSON_ASSERT_CONTAINS(doc, "routes"); JSON_ASSERT_ARRAY(doc["routes"]); const rapidjson::Value& routes_arr = doc["routes"]; for (rapidjson::Value::ConstValueIterator routes_it = routes_arr.Begin(); routes_it != routes_arr.End(); ++routes_it) { // An entry is valid if it has either a domain (string) OR a // number (string) AND an array of routes if ((((((*routes_it).HasMember("domain")) && ((*routes_it)["domain"].IsString())) && (!(*routes_it).HasMember("number"))) || ((!(*routes_it).HasMember("domain")) && (((*routes_it).HasMember("number")) && ((*routes_it)["number"].IsString())))) && ((*routes_it).HasMember("route") && (*routes_it)["route"].IsArray())) { std::vector<std::string> route_vec; const rapidjson::Value& route_arr = (*routes_it)["route"]; for (rapidjson::Value::ConstValueIterator route_it = route_arr.Begin(); route_it != route_arr.End(); ++route_it) { std::string route_uri = (*route_it).GetString(); TRC_DEBUG(" %s", route_uri.c_str()); route_vec.push_back(route_uri); } std::string routing_value; if ((*routes_it).HasMember("domain")) { routing_value = (*routes_it)["domain"].GetString(); new_domain_routes.insert(std::make_pair(routing_value, route_vec)); } else { routing_value = (*routes_it)["number"].GetString(); new_number_routes.insert( std::make_pair(PJUtils::remove_visual_separators(routing_value), route_vec)); } route_vec.clear(); TRC_DEBUG("Add route for %s", routing_value.c_str()); } else { TRC_WARNING("Badly formed BGCF route entry"); CL_SPROUT_BGCF_FILE_INVALID.log(); } } // Take a write lock on the mutex in RAII style boost::lock_guard<boost::shared_mutex> write_lock(_routes_rw_lock); _domain_routes = new_domain_routes; _number_routes = new_number_routes; } catch (JsonFormatError err) { TRC_ERROR("Badly formed BGCF configuration file - missing routes object"); CL_SPROUT_BGCF_FILE_INVALID.log(); } }
void MementoAppServerTsx::on_initial_request(pjsip_msg* req) { TRC_DEBUG("Memento processing an initial request of type %s", (req->line.req.method.id == PJSIP_INVITE_METHOD) ? "INVITE" : "BYE"); // Get the current time time_t rawtime; time(&rawtime); tm* start_time = localtime(&rawtime); _start_time_xml = create_formatted_timestamp(start_time, XML_PATTERN); _start_time_cassandra = create_formatted_timestamp(start_time, TIMESTAMP_PATTERN); // Is the call originating or terminating? std::string served_user; pjsip_routing_hdr* psu_hdr = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(req, &P_SERVED_USER, NULL); if (psu_hdr != NULL) { pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(&psu_hdr->name_addr); served_user = uri_to_string(PJSIP_URI_IN_ROUTING_HDR, uri); pjsip_param* sescase = pjsip_param_find(&psu_hdr->other_param, &SESCASE); if ((sescase != NULL) && (pj_stricmp(&sescase->value, &ORIG) == 0)) { TRC_DEBUG("Request is originating"); _outgoing = true; } } // Get the caller, callee and impu values if (_outgoing) { // Get the callee's URI amd name from the To header. _callee_uri = uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(req)->uri)); _callee_name = pj_str_to_string(&((pjsip_name_addr*) (PJSIP_MSG_TO_HDR(req)->uri))->display); // Get the caller's URI and name from the P-Asserted Identity header. If // this is missing, use the From header. pjsip_routing_hdr* asserted_id = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(req, &P_ASSERTED_IDENTITY, NULL); if (asserted_id != NULL) { _caller_uri = uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)pjsip_uri_get_uri(&asserted_id->name_addr)); _caller_name = pj_str_to_string(&asserted_id->name_addr.display); } else { TRC_WARNING("INVITE missing P-Asserted-Identity"); send_request(req); return; } // Set the IMPU equal to the caller's URI _impu = _caller_uri; } else { // Get the callee's URI from the request URI. There can be no name value. _callee_uri = uri_to_string(PJSIP_URI_IN_FROMTO_HDR, req->line.req.uri); // Get the caller's URI and name from the From header. _caller_uri = uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_FROM_HDR(req)->uri)); _caller_name = pj_str_to_string(&((pjsip_name_addr*) (PJSIP_MSG_FROM_HDR(req)->uri))->display); // Set the IMPU equal to the callee's URI _impu = _callee_uri; } // Add a unique ID containing the IMPU to the record route header. // This has the format: // <YYYYMMDDHHMMSS>_<unique_id>_<base64 encoded impu>.memento.<home domain> _unique_id = std::to_string(Utils::generate_unique_integer(0,0)); std::string encoded_impu = base64_encode(reinterpret_cast<const unsigned char*>(_impu.c_str()), _impu.length()); std::string dialog_id = std::string(_start_time_cassandra). append("_"). append(_unique_id). append("_"). append(encoded_impu); add_to_dialog(dialog_id); send_request(req); }
/// Selects the appropriate S-CSCF for the request, performing an HSS query /// if required. /// /// @param pool Pool to parse the SCSCF URI into. This must be valid /// for at least as long as the returned SCSCF URI. /// @param scscf_sip_uri Output parameter holding the parsed SCSCF URI. This /// is onle valid if the function returns PJSIP_SC_OK. int ICSCFRouter::get_scscf(pj_pool_t* pool, pjsip_sip_uri*& scscf_sip_uri) { int status_code = PJSIP_SC_OK; std::string scscf; scscf_sip_uri = NULL; if (!_queried_caps) { // Do the HSS query. status_code = hss_query(); // TS 32.260 table 5.2.1.1 says we should generate an ACR[Event] on // completion of a Cx Query. We therefore send the ACR here, but // leave it in place so we will send another ACR when the transaction // completes. (Note that TS 32.260 isn't clear on whether this ACR // should be generated if the Cx Query fails - we are sending on both // success and failure, but that could be wrong. Also, in the failure // case we will not include a Server-Capabilities AVP.) _acr->send(); } if (status_code == PJSIP_SC_OK) { if ((!_hss_rsp.scscf.empty()) && (std::find(_attempted_scscfs.begin(), _attempted_scscfs.end(), _hss_rsp.scscf) == _attempted_scscfs.end())) { // The HSS returned a S-CSCF name and it's not one we have tried // already. scscf = _hss_rsp.scscf; TRC_DEBUG("SCSCF specified by HSS: %s", scscf.c_str()); } else if (_queried_caps) { // We queried capabilities from the HSS, so select a suitable S-CSCF. scscf = _scscf_selector->get_scscf(_hss_rsp.mandatory_caps, _hss_rsp.optional_caps, _attempted_scscfs, _trail); TRC_DEBUG("SCSCF selected: %s", scscf.c_str()); } if (!scscf.empty()) { // Found an S-CSCF to try, so add it to the list of attempted S-CSCFs. _attempted_scscfs.push_back(scscf); // Check that the returned scscf is a valid SIP URI. pjsip_uri* scscf_uri = PJUtils::uri_from_string(scscf, pool); if ((scscf_uri != NULL) && PJSIP_URI_SCHEME_IS_SIP(scscf_uri)) { // Check whether the URI points back to ourselves, i.e. // - The host is either this server or the home domain. // - The port is the I-CSCF port for this deployment // // If the URI matches these criteria, we need to reject this message // now (with a signature SAS log) as this is never valid and would // lead to an infinite loop (were it not for our separate Max-Forwards // checking). // // The motivation for putting an explicit check here (rather than // relying on Max Forwards checking) is that this is reasonably likely // to occur when turning up a new deployment: customers can very easily // get their S-CSCF and I-CSCF ports the wrong way round (resulting in // an I-CSCF loop) and this fix will save them time diagnosing the // condition. // // Note that we are only checking the I-CSCF => I-CSCF loop condition // explicitly in this way. S-CSCF => S-CSCF loops are much harder to // explicitly block because messages can be legitimately routed by an // S-CSCF back to itself (with subtly changed headers) for various // reasons. Max Forwards checking should catch these instances. pjsip_sip_uri *sip_uri = (pjsip_sip_uri*)scscf_uri; URIClass uri_class = URIClassifier::classify_uri(scscf_uri); if (((uri_class == NODE_LOCAL_SIP_URI) || (uri_class == HOME_DOMAIN_SIP_URI)) && (sip_uri->port == _port)) { TRC_WARNING("SCSCF URI %s points back to ICSCF", scscf.c_str()); status_code = PJSIP_SC_LOOP_DETECTED; SAS::Event event(_trail, SASEvent::SCSCF_ICSCF_LOOP_DETECTED, 0); SAS::report_event(event); } else { scscf_sip_uri = sip_uri; } } else { TRC_WARNING("Invalid SCSCF URI %s", scscf.c_str()); status_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE; } } else { // Failed to select an S-CSCF providing all the mandatory parameters, // so return 600 Busy Everywhere response. status_code = PJSIP_SC_BUSY_EVERYWHERE; } } if (status_code == PJSIP_SC_OK) { SAS::Event event(_trail, SASEvent::SCSCF_SELECTION_SUCCESS, 0); event.add_var_param(scscf); event.add_var_param(_hss_rsp.scscf); SAS::report_event(event); } else { SAS::Event event(_trail, SASEvent::SCSCF_SELECTION_FAILED, 0); std::string st_code = std::to_string(status_code); event.add_var_param(st_code); SAS::report_event(event); } return status_code; }
/// Retrieve the data for a given namespace and key. Store::Status BaseMemcachedStore::get_data(const std::string& table, const std::string& key, std::string& data, uint64_t& cas, SAS::TrailId trail) { Store::Status status; // Construct the fully qualified key. std::string fqkey = table + "\\\\" + key; const char* key_ptr = fqkey.data(); const size_t key_len = fqkey.length(); int vbucket = vbucket_for_key(fqkey); const std::vector<memcached_st*>& replicas = get_replicas(vbucket, Op::READ); if (trail != 0) { SAS::Event start(trail, SASEvent::MEMCACHED_GET_START, 0); start.add_var_param(fqkey); SAS::report_event(start); } TRC_DEBUG("%d read replicas for key %s", replicas.size(), fqkey.c_str()); // Read from all replicas until we get a positive result. memcached_return_t rc = MEMCACHED_ERROR; bool active_not_found = false; size_t failed_replicas = 0; size_t ii; // If we only have one replica, we should try it twice - // libmemcached won't notice a dropped TCP connection until it tries // to make a request on it, and will fail the request then // reconnect, so the second attempt could still work. size_t attempts = (replicas.size() == 1) ? 2 : replicas.size(); for (ii = 0; ii < attempts; ++ii) { size_t replica_idx; if ((replicas.size() == 1) && (ii == 1)) { if (rc != MEMCACHED_CONNECTION_FAILURE) { // This is a legitimate error, not a server failure, so we // shouldn't retry. break; } replica_idx = 0; TRC_WARNING("Failed to read from sole memcached replica: retrying once"); } else { replica_idx = ii; } TRC_DEBUG("Attempt to read from replica %d (connection %p)", replica_idx, replicas[replica_idx]); rc = get_from_replica(replicas[replica_idx], key_ptr, key_len, data, cas); if (memcached_success(rc)) { // Got data back from this replica. Don't try any more. TRC_DEBUG("Read for %s on replica %d returned SUCCESS", fqkey.c_str(), replica_idx); break; } else if (rc == MEMCACHED_NOTFOUND) { // Failed to find a record on an active replica. Flag this so if we do // find data on a later replica we can reset the cas value returned to // zero to ensure a subsequent write will succeed. TRC_DEBUG("Read for %s on replica %d returned NOTFOUND", fqkey.c_str(), replica_idx); active_not_found = true; } else { // Error from this node, so consider it inactive. TRC_DEBUG("Read for %s on replica %d returned error %d (%s)", fqkey.c_str(), replica_idx, rc, memcached_strerror(replicas[replica_idx], rc)); ++failed_replicas; } } if (memcached_success(rc)) { if (data != TOMBSTONE) { if (trail != 0) { SAS::Event got_data(trail, SASEvent::MEMCACHED_GET_SUCCESS, 0); got_data.add_var_param(fqkey); got_data.add_var_param(data); got_data.add_static_param(cas); SAS::report_event(got_data); } // Return the data and CAS value. The CAS value is either set to the CAS // value from the result, or zero if an earlier active replica returned // NOT_FOUND. This ensures that a subsequent set operation will succeed // on the earlier active replica. if (active_not_found) { cas = 0; } TRC_DEBUG("Read %d bytes from table %s key %s, CAS = %ld", data.length(), table.c_str(), key.c_str(), cas); status = Store::OK; } else { if (trail != 0) { SAS::Event got_tombstone(trail, SASEvent::MEMCACHED_GET_TOMBSTONE, 0); got_tombstone.add_var_param(fqkey); got_tombstone.add_static_param(cas); SAS::report_event(got_tombstone); } // We have read a tombstone. Return NOT_FOUND to the caller, and also // zero out the CAS (returning a zero CAS makes the interface cleaner). TRC_DEBUG("Read tombstone from table %s key %s, CAS = %ld", table.c_str(), key.c_str(), cas); cas = 0; status = Store::NOT_FOUND; } // Regardless of whether we got a tombstone, the vbucket is alive. update_vbucket_comm_state(vbucket, OK); if (_comm_monitor) { _comm_monitor->inform_success(); } } else if (failed_replicas < replicas.size()) { // At least one replica returned NOT_FOUND. if (trail != 0) { SAS::Event not_found(trail, SASEvent::MEMCACHED_GET_NOT_FOUND, 0); not_found.add_var_param(fqkey); SAS::report_event(not_found); } TRC_DEBUG("At least one replica returned not found, so return NOT_FOUND"); status = Store::Status::NOT_FOUND; update_vbucket_comm_state(vbucket, OK); if (_comm_monitor) { _comm_monitor->inform_success(); } } else { // All replicas returned an error, so log the error and return the // failure. if (trail != 0) { SAS::Event err(trail, SASEvent::MEMCACHED_GET_ERROR, 0); err.add_var_param(fqkey); SAS::report_event(err); } TRC_ERROR("Failed to read data for %s from %d replicas", fqkey.c_str(), replicas.size()); status = Store::Status::ERROR; update_vbucket_comm_state(vbucket, FAILED); if (_comm_monitor) { _comm_monitor->inform_failure(); } } return status; }
bool decode_homestead_xml(const std::string public_user_identity, std::shared_ptr<rapidxml::xml_document<> > root, std::string& regstate, std::map<std::string, Ifcs >& ifcs_map, std::vector<std::string>& associated_uris, std::vector<std::string>& aliases, std::deque<std::string>& ccfs, std::deque<std::string>& ecfs, bool allowNoIMS) { if (!root.get()) { // If get_xml_object has not returned a document, there must have been a parsing error. TRC_WARNING("Malformed HSS XML - document couldn't be parsed"); return false; } rapidxml::xml_node<>* cw = root->first_node("ClearwaterRegData"); if (!cw) { TRC_WARNING("Malformed Homestead XML - no ClearwaterRegData element"); return false; } rapidxml::xml_node<>* reg = cw->first_node("RegistrationState"); if (!reg) { TRC_WARNING("Malformed Homestead XML - no RegistrationState element"); return false; } regstate = reg->value(); if ((regstate == HSSConnection::STATE_NOT_REGISTERED) && (allowNoIMS)) { TRC_DEBUG("Subscriber is not registered on a get_registration_state request"); return true; } rapidxml::xml_node<>* imss = cw->first_node("IMSSubscription"); if (!imss) { TRC_WARNING("Malformed HSS XML - no IMSSubscription element"); return false; } // The set of aliases consists of the set of public identities in the same // Service Profile. It is a subset of the associated URIs. In order to find // the set of aliases we want, we need to find the Service Profile containing // our public identity and then save off all of the public identities in this // Service Profile. // // sp_identities is used to save the public identities in the current Service // Profile. // current_sp_contains_public_id is a flag used to indicate that the Service // Profile we're currently cycling through contains our public identity. // found_aliases is a flag used to indicate that we've already found our list // of aliases. std::vector<std::string> sp_identities; bool current_sp_contains_public_id = false; bool found_aliases = false; rapidxml::xml_node<>* sp = NULL; if (!imss->first_node("ServiceProfile")) { TRC_WARNING("Malformed HSS XML - no ServiceProfiles"); return false; } for (sp = imss->first_node("ServiceProfile"); sp != NULL; sp = sp->next_sibling("ServiceProfile")) { Ifcs ifc(root, sp); rapidxml::xml_node<>* public_id = NULL; if (!sp->first_node("PublicIdentity")) { TRC_WARNING("Malformed ServiceProfile XML - no Public Identity"); return false; } for (public_id = sp->first_node("PublicIdentity"); public_id != NULL; public_id = public_id->next_sibling("PublicIdentity")) { rapidxml::xml_node<>* identity = public_id->first_node("Identity"); if (identity) { std::string uri = std::string(identity->value()); TRC_DEBUG("Processing Identity node from HSS XML - %s\n", uri.c_str()); associated_uris.push_back(uri); ifcs_map[uri] = ifc; if (!found_aliases) { sp_identities.push_back(uri); if (uri == public_user_identity) { current_sp_contains_public_id = true; } } } else { TRC_WARNING("Malformed PublicIdentity XML - no Identity"); return false; } } if (!found_aliases) { if (current_sp_contains_public_id) { aliases = sp_identities; found_aliases = true; } else { sp_identities.clear(); } } } rapidxml::xml_node<>* charging_addrs_node = cw->first_node("ChargingAddresses"); if (charging_addrs_node) { rapidxml::xml_node<>* ccf = NULL; std::vector<rapidxml::xml_node<>*> xml_ccfs; rapidxml::xml_node<>* ecf = NULL; std::vector<rapidxml::xml_node<>*> xml_ecfs; // Save off all of the CCF nodes so that we can sort them based on their // priority attribute. for (ccf = charging_addrs_node->first_node("CCF"); ccf != NULL; ccf = ccf->next_sibling("CCF")) { xml_ccfs.push_back(ccf); } // Sort them and add them to ccfs in order. std::sort(xml_ccfs.begin(), xml_ccfs.end(), compare_charging_addrs); for (std::vector<rapidxml::xml_node<>*>::iterator it = xml_ccfs.begin(); it != xml_ccfs.end(); ++it) { TRC_DEBUG("Found CCF: %s", (*it)->value()); ccfs.push_back((*it)->value()); } // Save off all of the ECF nodes so that we can sort them based on their // priority attribute. for (ecf = charging_addrs_node->first_node("ECF"); ecf != NULL; ecf = ecf->next_sibling("ECF")) { xml_ecfs.push_back(ecf); } // Sort them and add them to ecfs in order. std::sort(xml_ecfs.begin(), xml_ecfs.end(), compare_charging_addrs); for (std::vector<rapidxml::xml_node<>*>::iterator it = xml_ecfs.begin(); it != xml_ecfs.end(); ++it) { TRC_DEBUG("Found ECF: %s", (*it)->value()); ecfs.push_back((*it)->value()); } } return true; }
int init_options(int argc, char**argv, struct options& options) { int opt; int long_opt_ind; optind = 0; while ((opt = getopt_long(argc, argv, "", long_opt, &long_opt_ind)) != -1) { switch (opt) { case LOCAL_HOST: TRC_INFO("Local host: %s", optarg); options.local_host = std::string(optarg); break; case HTTP_ADDRESS: TRC_INFO("HTTP bind address: %s", optarg); options.http_address = std::string(optarg); break; case HTTP_THREADS: TRC_INFO("Number of HTTP threads: %s", optarg); options.http_threads = atoi(optarg); break; case HTTP_WORKER_THREADS: TRC_INFO("Number of HTTP worker threads: %s", optarg); options.http_worker_threads = atoi(optarg); break; case HOMESTEAD_HTTP_NAME: TRC_INFO("Homestead HTTP address: %s", optarg); options.homestead_http_name = std::string(optarg); break; case DIGEST_TIMEOUT: options.digest_timeout = atoi(optarg); if (options.digest_timeout == 0) { // If the supplied option is invalid then revert to the // default five minutes options.digest_timeout = 300; } TRC_INFO("Digest timeout: %s", optarg); break; case HOME_DOMAIN: options.home_domain = std::string(optarg); TRC_INFO("Home domain: %s", optarg); break; case SAS_CONFIG: { std::vector<std::string> sas_options; Utils::split_string(std::string(optarg), ',', sas_options, 0, false); if ((sas_options.size() == 2) && !sas_options[0].empty() && !sas_options[1].empty()) { options.sas_server = sas_options[0]; options.sas_system_name = sas_options[1]; TRC_INFO("SAS set to %s\n", options.sas_server.c_str()); TRC_INFO("System name is set to %s\n", options.sas_system_name.c_str()); } else { TRC_INFO("Invalid --sas option, SAS disabled\n"); } } break; case ACCESS_LOG: TRC_INFO("Access log: %s", optarg); options.access_log_enabled = true; options.access_log_directory = std::string(optarg); break; case MEMCACHED_WRITE_FORMAT: if (strcmp(optarg, "binary") == 0) { TRC_INFO("Memcached write format set to 'binary'"); options.memcached_write_format = MemcachedWriteFormat::BINARY; } else if (strcmp(optarg, "json") == 0) { TRC_INFO("Memcached write format set to 'json'"); options.memcached_write_format = MemcachedWriteFormat::JSON; } else { TRC_WARNING("Invalid value for memcached-write-format, using '%s'." "Got '%s', valid vales are 'json' and 'binary'", ((options.memcached_write_format == MemcachedWriteFormat::JSON) ? "json" : "binary"), optarg); } break; case TARGET_LATENCY_US: options.target_latency_us = atoi(optarg); if (options.target_latency_us <= 0) { TRC_ERROR("Invalid --target-latency-us option %s", optarg); return -1; } break; case MAX_TOKENS: options.max_tokens = atoi(optarg); if (options.max_tokens <= 0) { TRC_ERROR("Invalid --max-tokens option %s", optarg); return -1; } break; case INIT_TOKEN_RATE: options.init_token_rate = atoi(optarg); if (options.init_token_rate <= 0) { TRC_ERROR("Invalid --init-token-rate option %s", optarg); return -1; } break; case MIN_TOKEN_RATE: options.min_token_rate = atoi(optarg); if (options.min_token_rate <= 0) { TRC_ERROR("Invalid --min-token-rate option %s", optarg); return -1; } break; case EXCEPTION_MAX_TTL: options.exception_max_ttl = atoi(optarg); TRC_INFO("Max TTL after an exception set to %d", options.exception_max_ttl); break; case HTTP_BLACKLIST_DURATION: options.http_blacklist_duration = atoi(optarg); TRC_INFO("HTTP blacklist duration set to %d", options.http_blacklist_duration); break; case API_KEY: options.api_key = std::string(optarg); TRC_INFO("HTTP API key set to %s", options.api_key.c_str()); break; case PIDFILE: options.pidfile = std::string(optarg); break; case DAEMON: options.daemon = true; break; case LOG_FILE: case LOG_LEVEL: // Ignore these options - they're handled by init_logging_options break; case HELP: usage(); return -1; default: TRC_ERROR("Unknown option. Run with --help for options.\n"); return -1; } } return 0; }
void HTTPCallback::worker_thread_entry_point() { CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); Timer* timer; while (_q.pop(timer)) { // Set up the request details. curl_easy_setopt(curl, CURLOPT_URL, timer->callback_url.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, timer->callback_body.data()); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, timer->callback_body.length()); // Include the sequence number header. struct curl_slist* headers = NULL; headers = curl_slist_append(headers, (std::string("X-Sequence-Number: ") + std::to_string(timer->sequence_number)).c_str()); headers = curl_slist_append(headers, "Content-Type: application/octet-stream"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Send the request CURLcode curl_rc = curl_easy_perform(curl); if (curl_rc == CURLE_OK) { // Check if the next pop occurs before the repeat-for interval and, // if not, convert to a tombstone to indicate the timer is dead. if ((timer->sequence_number + 1) * timer->interval > timer->repeat_for) { timer->become_tombstone(); } _replicator->replicate(timer); _handler->add_timer(timer); timer = NULL; // We relinquish control of the timer when we give // it to the store. if (_timer_pop_alarm) { _timer_pop_alarm->clear(); } } else { if (curl_rc == CURLE_HTTP_RETURNED_ERROR) { long http_rc = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_rc); TRC_WARNING("Got HTTP error %d from %s", http_rc, timer->callback_url.c_str()); } TRC_WARNING("Failed to process callback for %lu: URL %s, curl error was: %s", timer->id, timer->callback_url.c_str(), curl_easy_strerror(curl_rc)); if (_timer_pop_alarm && timer->is_last_replica()) { _timer_pop_alarm->set(); } delete timer; } // Tidy up request-speciifc objects curl_slist_free_all(headers); headers = NULL; } // Tidy up thread-specific objects curl_easy_cleanup(curl); return; }
void JSONEnumService::update_enum() { // Check whether the file exists. struct stat s; if ((stat(_configuration.c_str(), &s) != 0) && (errno == ENOENT)) { TRC_STATUS("No ENUM configuration (file %s does not exist)", _configuration.c_str()); CL_SPROUT_ENUM_FILE_MISSING.log(_configuration.c_str()); return; } TRC_STATUS("Loading ENUM configuration from %s", _configuration.c_str()); // Read from the file std::ifstream fs(_configuration.c_str()); std::string enum_str((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>()); if (enum_str == "") { // LCOV_EXCL_START TRC_ERROR("Failed to read ENUM configuration data from %s", _configuration.c_str()); CL_SPROUT_ENUM_FILE_EMPTY.log(_configuration.c_str()); return; // LCOV_EXCL_STOP } // Now parse the document rapidjson::Document doc; doc.Parse<0>(enum_str.c_str()); if (doc.HasParseError()) { TRC_ERROR("Failed to read ENUM configuration data: %s\nError: %s", enum_str.c_str(), rapidjson::GetParseError_En(doc.GetParseError())); CL_SPROUT_ENUM_FILE_INVALID.log(_configuration.c_str()); return; } try { std::vector<NumberPrefix> new_number_prefixes; std::map<std::string, NumberPrefix> new_prefix_regex_map; JSON_ASSERT_CONTAINS(doc, "number_blocks"); JSON_ASSERT_ARRAY(doc["number_blocks"]); const rapidjson::Value& nb_arr = doc["number_blocks"]; for (rapidjson::Value::ConstValueIterator nb_it = nb_arr.Begin(); nb_it != nb_arr.End(); ++nb_it) { try { std::string prefix; JSON_GET_STRING_MEMBER(*nb_it, "prefix", prefix); std::string regex; JSON_GET_STRING_MEMBER(*nb_it, "regex", regex); // Entry is well-formed, so strip off visual separators and add it. TRC_DEBUG("Found valid number prefix block %s", prefix.c_str()); NumberPrefix pfix; prefix = PJUtils::remove_visual_separators(prefix); pfix.prefix = prefix; if (parse_regex_replace(regex, pfix.match, pfix.replace)) { // Create an array in order of entries in json file, and a map // (automatically sorted in order of key length) so we can later // match numbers to the most specific prefixes new_number_prefixes.push_back(pfix); new_prefix_regex_map.insert(std::make_pair(prefix, pfix)); TRC_STATUS(" Adding number prefix %s, regex=%s", pfix.prefix.c_str(), regex.c_str()); } else { TRC_WARNING("Badly formed regular expression in ENUM number block %s", regex.c_str()); } } catch (JsonFormatError err) { // Badly formed number block. TRC_WARNING("Badly formed ENUM number block (hit error at %s:%d)", err._file, err._line); CL_SPROUT_ENUM_FILE_INVALID.log(_configuration.c_str()); } } // Take a write lock on the mutex in RAII style boost::lock_guard<boost::shared_mutex> write_lock(_number_prefixes_rw_lock); _number_prefixes = new_number_prefixes; _prefix_regex_map = new_prefix_regex_map; } catch (JsonFormatError err) { TRC_ERROR("Badly formed ENUM configuration data - missing number_blocks object"); CL_SPROUT_ENUM_FILE_INVALID.log(_configuration.c_str()); } }
int init_options(int argc, char**argv, struct options& options) { int opt; int long_opt_ind; optind = 0; while ((opt = getopt_long(argc, argv, options_description.c_str(), long_opt, &long_opt_ind)) != -1) { switch (opt) { case 'l': TRC_INFO("Local host: %s", optarg); options.local_host = std::string(optarg); break; case 'r': TRC_INFO("Home domain: %s", optarg); options.home_domain = std::string(optarg); break; case 'c': TRC_INFO("Diameter configuration file: %s", optarg); options.diameter_conf = std::string(optarg); break; case 'H': TRC_INFO("HTTP address: %s", optarg); options.http_address = std::string(optarg); break; case 't': TRC_INFO("HTTP threads: %s", optarg); options.http_threads = atoi(optarg); break; case 'u': TRC_INFO("Cache threads: %s", optarg); options.cache_threads = atoi(optarg); break; case 'S': TRC_INFO("Cassandra host: %s", optarg); options.cassandra = std::string(optarg); break; case 'D': TRC_INFO("Destination realm: %s", optarg); options.dest_realm = std::string(optarg); break; case 'd': TRC_INFO("Destination host: %s", optarg); options.dest_host = std::string(optarg); break; case 'p': TRC_INFO("Maximum peers: %s", optarg); options.max_peers = atoi(optarg); break; case 's': TRC_INFO("Server name: %s", optarg); options.server_name = std::string(optarg); break; case 'i': TRC_INFO("IMPU cache TTL: %s", optarg); options.impu_cache_ttl = atoi(optarg); break; case 'I': TRC_INFO("HSS reregistration time: %s", optarg); options.hss_reregistration_time = atoi(optarg); break; case 'j': TRC_INFO("Sprout HTTP name: %s", optarg); options.sprout_http_name = std::string(optarg); break; case SCHEME_UNKNOWN: TRC_INFO("Scheme unknown: %s", optarg); options.scheme_unknown = std::string(optarg); break; case SCHEME_DIGEST: TRC_INFO("Scheme digest: %s", optarg); options.scheme_digest = std::string(optarg); break; case SCHEME_AKA: TRC_INFO("Scheme AKA: %s", optarg); options.scheme_aka = std::string(optarg); break; case 'a': TRC_INFO("Access log: %s", optarg); options.access_log_enabled = true; options.access_log_directory = std::string(optarg); break; case SAS_CONFIG: { std::vector<std::string> sas_options; Utils::split_string(std::string(optarg), ',', sas_options, 0, false); if (sas_options.size() == 2) { options.sas_server = sas_options[0]; options.sas_system_name = sas_options[1]; TRC_INFO("SAS set to %s\n", options.sas_server.c_str()); TRC_INFO("System name is set to %s\n", options.sas_system_name.c_str()); } else { CL_HOMESTEAD_INVALID_SAS_OPTION.log(); TRC_WARNING("Invalid --sas option, SAS disabled\n"); } } break; case DIAMETER_TIMEOUT_MS: TRC_INFO("Diameter timeout: %s", optarg); options.diameter_timeout_ms = atoi(optarg); break; case ALARMS_ENABLED: TRC_INFO("SNMP alarms are enabled"); options.alarms_enabled = true; break; case DNS_SERVER: options.dns_servers.clear(); Utils::split_string(std::string(optarg), ',', options.dns_servers, 0, false); TRC_INFO("%d DNS servers passed on the command line", options.dns_servers.size()); break; case TARGET_LATENCY_US: options.target_latency_us = atoi(optarg); if (options.target_latency_us <= 0) { TRC_ERROR("Invalid --target-latency-us option %s", optarg); return -1; } break; case MAX_TOKENS: options.max_tokens = atoi(optarg); if (options.max_tokens <= 0) { TRC_ERROR("Invalid --max-tokens option %s", optarg); return -1; } break; case INIT_TOKEN_RATE: options.init_token_rate = atoi(optarg); if (options.init_token_rate <= 0) { TRC_ERROR("Invalid --init-token-rate option %s", optarg); return -1; } break; case MIN_TOKEN_RATE: options.min_token_rate = atoi(optarg); if (options.min_token_rate <= 0) { TRC_ERROR("Invalid --min-token-rate option %s", optarg); return -1; } break; case EXCEPTION_MAX_TTL: options.exception_max_ttl = atoi(optarg); TRC_INFO("Max TTL after an exception set to %d", options.exception_max_ttl); break; case HTTP_BLACKLIST_DURATION: options.http_blacklist_duration = atoi(optarg); TRC_INFO("HTTP blacklist duration set to %d", options.http_blacklist_duration); break; case DIAMETER_BLACKLIST_DURATION: options.diameter_blacklist_duration = atoi(optarg); TRC_INFO("Diameter blacklist duration set to %d", options.diameter_blacklist_duration); break; case 'F': case 'L': // Ignore F and L - these are handled by init_logging_options break; case 'h': usage(); return -1; default: CL_HOMESTEAD_INVALID_OPTION_C.log(opt); TRC_ERROR("Unknown option. Run with --help for options.\n"); return -1; } } return 0; }
void MementoAppServerTsx::on_in_dialog_request(pjsip_msg* req) { TRC_DEBUG("Mememto processing an in_dialog_request"); // Get the dialog id. It has the format: // <YYYYMMDDHHMMSS>_<unique_id>_<base64 encoded impu> std::string dialogid = dialog_id(); // Pull out the timestamp, id and IMPU. std::vector<std::string> dialog_values; Utils::split_string(dialogid, '_', dialog_values, 3, false); if (dialog_values.size() != 3) { // LCOV_EXCL_START TRC_WARNING("Invalid dialog ID (%s)", dialogid.c_str()); send_request(req); return; // LCOV_EXCL_STOP } std::string timestamp = dialog_values[0]; _unique_id = dialog_values[1]; _impu = base64_decode(dialog_values[2]); // Create the XML. XML should be of the form: // <end-time><current time></end-time> // Get the current time time_t currenttime; time(¤ttime); tm* ct = localtime(¤ttime); // Create the XML rapidxml::xml_document<> doc; std::string end_timestamp = create_formatted_timestamp(ct, XML_PATTERN); rapidxml::xml_node<>* root = doc.allocate_node( rapidxml::node_element, MementoXML::END_TIME, doc.allocate_string(end_timestamp.c_str())); doc.append_node(root); char contents[MAX_CALL_ENTRY_LENGTH] = {0}; char* end = rapidxml::print(contents, doc); *end = 0; // Write the call list entry to the call list store. SAS::Event event(trail(), SASEvent::CALL_LIST_END_FRAGMENT, 0); SAS::report_event(event); _call_list_store_processor->write_call_list_entry( _impu, timestamp, _unique_id, CallListStore::CallFragment::Type::END, contents, trail()); send_request(req); }
bool decode_homestead_xml(const std::string public_user_identity, std::shared_ptr<rapidxml::xml_document<> > root, std::string& regstate, std::map<std::string, Ifcs >& ifcs_map, AssociatedURIs& associated_uris, std::vector<std::string>& aliases, std::deque<std::string>& ccfs, std::deque<std::string>& ecfs, SIFCService* sifc_service, bool allowNoIMS, SAS::TrailId trail) { if (!root.get()) { // If get_xml_object has not returned a document, there must have been a parsing error. TRC_WARNING("Malformed HSS XML - document couldn't be parsed"); return false; } rapidxml::xml_node<>* cw = root->first_node(RegDataXMLUtils::CLEARWATER_REG_DATA); if (!cw) { TRC_WARNING("Malformed Homestead XML - no ClearwaterRegData element"); return false; } rapidxml::xml_node<>* reg = cw->first_node(RegDataXMLUtils::REGISTRATION_STATE); if (!reg) { TRC_WARNING("Malformed Homestead XML - no RegistrationState element"); return false; } regstate = reg->value(); if ((regstate == RegDataXMLUtils::STATE_NOT_REGISTERED) && (allowNoIMS)) { TRC_DEBUG("Subscriber is not registered on a get_registration_state request"); return true; } rapidxml::xml_node<>* imss = cw->first_node(RegDataXMLUtils::IMS_SUBSCRIPTION); if (!imss) { TRC_WARNING("Malformed HSS XML - no IMSSubscription element"); return false; } // The set of aliases consists of the set of public identities in the same // Service Profile. It is a subset of the associated URIs. // In order to find the set of aliases we want, we need to find the Service // Profile containing our public identity and then save off all of the public // identities in this Service Profile. // There are five types of public identity, and different ways to check if // they match our identity. // Distinct IMPU, non distinct/specific IMPU, Distinct PSI - If we get a // match against one of these, then this is definitely the correct // identity, and we stop looking for a match. // Wildcarded IMPU - Regex matching the IMPU. If we get a match we might be // in the correct service profile, but there could be a matching // distinct/non-distinct IMPU later. It's a misconfiguration to have // multiple wildcards that match an IMPU without having a distinct/non- // distinct IMPU as well. // Wildcarded PSI - Regex matching the IMPU. There's no way to indicate // what regex is the correct regex to match against the IMPU if there // are overlapping ranges in the user data (but this makes no sense // for a HSS to return, unlike for overlapping ranges for wildcard // IMPUs). We allow distinct PSIs to trump wildcard matches, otherwise // the first match is the one we take. // // sp_identities is used to save the public identities in the current Service // Profile. // current_sp_contains_public_id is a flag used to indicate that the // Service Profile we're currently cycling through definitely contains our // public identity (e.g. it wasn't found by matching a wildcard). // current_sp_maybe_contains_public_id is a flag used to indicate that the // Service Profile we're currently cycling through might contain our public // identity (e.g. it matched on a regex, but there could still be a non // wildcard match to come). // found_aliases is a flag used to indicate that we've already found our list // of aliases, maybe_found_aliases indicates that we might have found it, but // it could be overridden later. // wildcard_uri saves of the value of a wildcard identity that potentially // matches the public identity, so that we can update the barring state of // the public identity if the wildcard identity is the best match after we've // looked at all the service profiles. std::vector<std::string> sp_identities; std::vector<std::string> temp_aliases; bool current_sp_contains_public_id = false; bool current_sp_maybe_contains_public_id = false; bool found_aliases = false; bool maybe_found_aliases = false; bool found_multiple_matches = false; std::string wildcard_uri; associated_uris.clear_uris(); rapidxml::xml_node<>* sp = NULL; if (!imss->first_node(RegDataXMLUtils::SERVICE_PROFILE)) { TRC_WARNING("Malformed HSS XML - no ServiceProfiles"); return false; } for (sp = imss->first_node(RegDataXMLUtils::SERVICE_PROFILE); sp != NULL; sp = sp->next_sibling(RegDataXMLUtils::SERVICE_PROFILE)) { Ifcs ifc(root, sp, sifc_service, trail); rapidxml::xml_node<>* public_id = NULL; if (!sp->first_node(RegDataXMLUtils::PUBLIC_IDENTITY)) { TRC_WARNING("Malformed ServiceProfile XML - no Public Identity"); return false; } for (public_id = sp->first_node(RegDataXMLUtils::PUBLIC_IDENTITY); public_id != NULL; public_id = public_id->next_sibling(RegDataXMLUtils::PUBLIC_IDENTITY)) { rapidxml::xml_node<>* identity = public_id->first_node(RegDataXMLUtils::IDENTITY); if (identity) { // There are two potential identities in the Identity node: // - identity_uri: Identity used for matching against identities to // select the correct service profile. // - associated_uri: The actual associated URI. // // These identities are normally the same, except in the case of a // non-distinct IMPU (an IMPU that is part of a wildcard range, but is // explicitly included in the XML), where the identity_uri is the // distinct IMPU, and the associated_uri is the wildcard IMPU. std::string identity_uri = std::string(identity->value()); std::string associated_uri = identity_uri; rapidxml::xml_node<>* extension = public_id->first_node(RegDataXMLUtils::EXTENSION); if (extension) { RegDataXMLUtils::parse_extension_identity(associated_uri, extension); } rapidxml::xml_node<>* barring_indication = public_id->first_node(RegDataXMLUtils::BARRING_INDICATION); TRC_DEBUG("Processing Identity node from HSS XML - %s", identity_uri.c_str()); bool barred = false; if (barring_indication) { std::string value = barring_indication->value(); if (value == RegDataXMLUtils::STATE_BARRED) { barred = true; } } if (associated_uri != identity_uri) { // We're in the case where we're processing a non-distinct IMPU. We // don't want to handle updating the associated URI, as this should // be covered when we handle the corresponding wildcard IMPU entry. // Instead, store off any barring information for the IMPU as this // needs to override the barring status of the wildcard IMPU. associated_uris.add_barring_status(identity_uri, barred); } else if (!associated_uris.contains_uri(associated_uri)) { associated_uris.add_uri(associated_uri, barred); ifcs_map[associated_uri] = ifc; } if (!found_aliases) { sp_identities.push_back(associated_uri); if (identity_uri == public_user_identity) { current_sp_contains_public_id = true; } else if (WildcardUtils::check_users_equivalent( identity_uri, public_user_identity)) { found_multiple_matches = maybe_found_aliases; current_sp_maybe_contains_public_id = true; if (!maybe_found_aliases) { ifcs_map[public_user_identity] = ifc; wildcard_uri = identity_uri; } } } } else { TRC_WARNING("Malformed PublicIdentity XML - no Identity"); return false; } } if ((!found_aliases) && (current_sp_contains_public_id)) { aliases = sp_identities; found_aliases = true; } else if ((!found_multiple_matches) && (current_sp_maybe_contains_public_id)) { temp_aliases = sp_identities; maybe_found_aliases = true; } else { sp_identities.clear(); } } if (aliases.empty()) { if (!temp_aliases.empty()) { // The best match was a wildcard. aliases = temp_aliases; associated_uris.add_wildcard_mapping(wildcard_uri, public_user_identity); if (found_multiple_matches) { SAS::Event event(trail, SASEvent::AMBIGUOUS_WILDCARD_MATCH, 0); event.add_var_param(public_user_identity); SAS::report_event(event); } } else { SAS::Event event(trail, SASEvent::NO_MATCHING_SERVICE_PROFILE, 0); event.add_var_param(public_user_identity); SAS::report_event(event); } } rapidxml::xml_node<>* charging_addrs_node = cw->first_node("ChargingAddresses"); if (charging_addrs_node) { parse_charging_addrs_node(charging_addrs_node, ccfs, ecfs); } return true; }
void TimerHandler::add_timer(Timer* timer, bool update_stats) { pthread_mutex_lock(&_mutex); bool will_add_timer = true; // Convert the new timer to a timer pair TimerPair new_tp; new_tp.active_timer = timer; // Pull out any existing timer pair from the timer store TimerPair existing_tp; bool timer_found = _store->fetch(timer->id, existing_tp); // We've found a timer. if (timer_found) { std::string cluster_view_id; __globals->get_cluster_view_id(cluster_view_id); if ((timer->is_matching_cluster_view_id(cluster_view_id)) && !(existing_tp.active_timer->is_matching_cluster_view_id(cluster_view_id))) { // If the new timer matches the current cluster view ID, and the old timer // doesn't, always prioritise the new timer. TRC_DEBUG("Adding timer with current cluster view ID"); } else if (timer->sequence_number == existing_tp.active_timer->sequence_number) { // If the new timer has the same sequence number as the old timer, // then check which timer is newer. If the existing timer is newer then we just // want to replace the timer and not change it if (Utils::overflow_less_than(timer->start_time_mono_ms, existing_tp.active_timer->start_time_mono_ms)) { TRC_DEBUG("Timer sequence numbers the same, but timer is older than the " "timer in the store"); delete new_tp.active_timer; new_tp.active_timer = new Timer(*existing_tp.active_timer); if (existing_tp.information_timer) { new_tp.information_timer = new Timer(*existing_tp.information_timer); } will_add_timer = false; } else { TRC_DEBUG("Adding timer as it's newer than the timer in the store"); } } else { // One of the sequence numbers is non-zero - at least one request is not // from the client if ((near_time(timer->start_time_mono_ms, existing_tp.active_timer->start_time_mono_ms)) && (timer->sequence_number < existing_tp.active_timer->sequence_number) && (timer->sequence_number != 0)) { // These are probably the same timer, and the timer we are trying to add is both // not from the client, and has a lower sequence number (so is less "informed") TRC_DEBUG("Not adding timer as it's older than the timer in the store"); delete new_tp.active_timer; new_tp.active_timer = new Timer(*existing_tp.active_timer); if (existing_tp.information_timer) { new_tp.information_timer = new Timer(*existing_tp.information_timer); } will_add_timer = false; } else { TRC_DEBUG("Adding timer as it's newer than the timer in the store"); } } // We're adding the new timer (not just replacing an existing timer) if (will_add_timer) { // If the new timer is a tombstone, make sure its interval is long enough save_tombstone_information(new_tp.active_timer, existing_tp.active_timer); // Decide whether we should save the old timer as an informational timer if (existing_tp.active_timer->cluster_view_id != new_tp.active_timer->cluster_view_id) { // The cluster IDs on the new and existing timers are different. // This means that the cluster configuration has changed between // then and when the timer was last updated TRC_DEBUG("Saving existing timer as informational timer"); if (existing_tp.information_timer) { // There's already a saved timer, but the new timer doesn't match the // existing timer. This is an error condition, and suggests that // a scaling operation has been started before an old scaling operation // finished, or there was a node failure during a scaling operation. // Either way, the saved timer information is out of date, and is // deleted (by not saving a copy of it when we delete the entire Timer // ID structure in the next step) TRC_WARNING("Deleting out of date timer from timer map"); } new_tp.information_timer = new Timer(*existing_tp.active_timer); } else if (existing_tp.information_timer) { // If there's an existing informational timer save it off new_tp.information_timer = new Timer(*existing_tp.information_timer); } } } else { TRC_DEBUG("Adding new timer"); } // Would be good in future work to pull all statistics logic out into a // separate statistics module, passing in new and old tags, and what is // happening to the timer (add, update, delete), to keep the timer_handler // scope of responsibility clear. // Update statistics if (update_stats) { std::vector<std::string> tags_to_add = std::vector<std::string>(); std::vector<std::string> tags_to_remove = std::vector<std::string>(); if (new_tp.active_timer->is_tombstone()) { // If the new timer is a tombstone, no new tags should be added // If it overwrites an existing active timer, the old tags should // be removed, and global count decremented if ((existing_tp.active_timer) && !(existing_tp.active_timer->is_tombstone())) { tags_to_remove = existing_tp.active_timer->tags; TRC_DEBUG("new timer is a tombstone overwriting an existing timer"); _all_timers_table->decrement(1); } } else { // Add new timer tags tags_to_add = new_tp.active_timer->tags; // If there was an old existing timer, its tags should be removed // Global count should only increment if there was not an old // timer, as otherwise it is only an update. if ((existing_tp.active_timer) && !(existing_tp.active_timer->is_tombstone())) { tags_to_remove = existing_tp.active_timer->tags; } else { TRC_DEBUG("New timer being added, and no existing timer"); _all_timers_table->increment(1); } } update_statistics(tags_to_add, tags_to_remove); } delete existing_tp.active_timer; delete existing_tp.information_timer; TimerID id = new_tp.active_timer->id; uint32_t next_pop_time = new_tp.active_timer->next_pop_time(); std::vector<std::string> cluster_view_id_vector; cluster_view_id_vector.push_back(new_tp.active_timer->cluster_view_id); if (new_tp.information_timer) { cluster_view_id_vector.push_back(new_tp.information_timer->cluster_view_id); } TRC_DEBUG("Inserting the new timer with ID %llu", id); _store->insert(new_tp, id, next_pop_time, cluster_view_id_vector); pthread_mutex_unlock(&_mutex); }
void SCSCFSelector::update_scscf() { // Check whether the file exists. struct stat s; if ((stat(_configuration.c_str(), &s) != 0) && (errno == ENOENT)) { TRC_STATUS("No S-CSCF configuration data (file %s does not exist)", _configuration.c_str()); CL_SPROUT_SCSCF_FILE_MISSING.log(); return; } TRC_STATUS("Loading S-CSCF configuration from %s", _configuration.c_str()); // Read from the file std::ifstream fs(_configuration.c_str()); std::string scscf_str((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>()); if (scscf_str == "") { // LCOV_EXCL_START TRC_ERROR("Failed to read S-CSCF configuration data from %s", _configuration.c_str()); CL_SPROUT_SCSCF_FILE_EMPTY.log(); return; // LCOV_EXCL_STOP } // Now parse the document rapidjson::Document doc; doc.Parse<0>(scscf_str.c_str()); if (doc.HasParseError()) { TRC_ERROR("Failed to read S-CSCF configuration data: %s\nError: %s", scscf_str.c_str(), rapidjson::GetParseError_En(doc.GetParseError())); CL_SPROUT_SCSCF_FILE_INVALID.log(); return; } try { std::vector<scscf_t> new_scscfs; JSON_ASSERT_CONTAINS(doc, "s-cscfs"); JSON_ASSERT_ARRAY(doc["s-cscfs"]); const rapidjson::Value& scscfs_arr = doc["s-cscfs"]; for (rapidjson::Value::ConstValueIterator scscfs_it = scscfs_arr.Begin(); scscfs_it != scscfs_arr.End(); ++scscfs_it) { try { scscf_t new_scscf; JSON_GET_STRING_MEMBER(*scscfs_it, "server", new_scscf.server); JSON_GET_INT_MEMBER(*scscfs_it, "priority", new_scscf.priority); JSON_GET_INT_MEMBER(*scscfs_it, "weight", new_scscf.weight); JSON_ASSERT_CONTAINS(*scscfs_it, "capabilities"); JSON_ASSERT_ARRAY((*scscfs_it)["capabilities"]); const rapidjson::Value& cap_arr = (*scscfs_it)["capabilities"]; std::vector<int> capabilities_vec; for (rapidjson::Value::ConstValueIterator cap_it = cap_arr.Begin(); cap_it != cap_arr.End(); ++cap_it) { capabilities_vec.push_back((*cap_it).GetInt()); } // Sort the capabilities and remove duplicates std::sort(capabilities_vec.begin(), capabilities_vec.end()); capabilities_vec.erase(unique(capabilities_vec.begin(), capabilities_vec.end()), capabilities_vec.end() ); new_scscf.capabilities = capabilities_vec; new_scscfs.push_back(new_scscf); capabilities_vec.clear(); } catch (JsonFormatError err) { // Badly formed number block. TRC_WARNING("Badly formed S-CSCF entry (hit error at %s:%d)", err._file, err._line); CL_SPROUT_SCSCF_FILE_INVALID.log(); } } // Take a write lock on the mutex in RAII style boost::lock_guard<boost::shared_mutex> write_lock(_scscfs_rw_lock); _scscfs = new_scscfs; } catch (JsonFormatError err) { TRC_ERROR("Badly formed S-CSCF configuration file - missing s-cscfs object"); CL_SPROUT_SCSCF_FILE_INVALID.log(); } }
void ICSCFSproutletRegTsx::on_rx_initial_request(pjsip_msg* req) { // Create an ACR for this transaction. _acr = _icscf->get_acr(trail()); _acr->rx_request(req); TRC_DEBUG("I-CSCF initialize transaction for REGISTER request"); // Extract relevant fields from the message std::string impu; std::string impi; std::string visited_network; std::string auth_type; // Get the public identity from the To: header. pjsip_to_hdr* to_hdr = PJSIP_MSG_TO_HDR(req); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(to_hdr->uri); impu = PJUtils::public_id_from_uri(to_uri); SAS::Event reg_event(trail(), SASEvent::ICSCF_RCVD_REGISTER, 0); reg_event.add_var_param(impu); SAS::report_event(reg_event); // Get the private identity from the Authentication header, or generate // a default if there is no Authentication header or no username in the // header. pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_AUTHORIZATION, NULL); if ((auth_hdr != NULL) && (auth_hdr->credential.digest.username.slen != 0)) { // Get the IMPI from the username. impi = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username); } else { // Create a default IMPI from the IMPU by removing the sip: prefix. impi = impu.substr(4); } // Get the visited network identification if present. If not, warn // (because a spec-compliant P-CSCF should default it) and use a // sensible default. pjsip_generic_string_hdr* vn_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(req, &STR_P_V_N_I, NULL); if (vn_hdr != NULL) { visited_network = PJUtils::pj_str_to_string(&vn_hdr->hvalue); } else if (PJSIP_URI_SCHEME_IS_SIP(to_uri) || PJSIP_URI_SCHEME_IS_SIPS(to_uri)) { // Use the domain of the IMPU as the visited network. visited_network = PJUtils::pj_str_to_string(&((pjsip_sip_uri*)to_uri)->host); TRC_WARNING("No P-Visited-Network-ID in REGISTER - using %s as a default", visited_network.c_str()); } // Work out what authorization type to use by looking at the expiry // values in the request. (Use a default of 1 because if there is no // expires header or expires values in the contact headers this will // be a registration not a deregistration.) auth_type = PJUtils::is_deregistration(req) ? "DEREG" : "REG"; // Remove any Route headers present on the request as we're re-routing the // message. pj_str_t route_hdr_name = pj_str((char *)"Route"); PJUtils::remove_hdr(req, &route_hdr_name); // Check if this is an emergency registration. This is true if any of the // contact headers in the message contain the "sos" parameter. bool emergency = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { if (PJUtils::is_emergency_registration(contact_hdr)) { emergency = true; break; } contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, contact_hdr->next); } // Create an UAR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFUARouter(_icscf->get_hss_connection(), _icscf->get_scscf_selector(), trail(), _acr, _icscf->port(), impi, impu, visited_network, auth_type, emergency); // We have a router, query it for an S-CSCF to use. pjsip_sip_uri* scscf_sip_uri = NULL; std::string dummy_wildcard; pjsip_status_code status_code = (pjsip_status_code)_router->get_scscf(get_pool(req), scscf_sip_uri, dummy_wildcard); if (status_code == PJSIP_SC_OK) { TRC_DEBUG("Found SCSCF for REGISTER"); req->line.req.uri = (pjsip_uri*)scscf_sip_uri; send_request(req); } else { pjsip_msg* rsp = create_response(req, status_code); send_response(rsp); free_msg(req); } }
std::string DNSEnumService::lookup_uri_from_user(const std::string& user, SAS::TrailId trail) const { if (user.empty()) { TRC_INFO("No dial string supplied, so don't do ENUM lookup"); return std::string(); } // Log starting ENUM processing. SAS::Event event(trail, SASEvent::ENUM_START, 0); event.add_var_param(user); SAS::report_event(event); // Determine the Application Unique String (AUS) from the user. This is // used to form the first key, and also as the input into the regular // expressions. std::string aus = user_to_aus(user); std::string string = aus; // Get the resolver to use. This comes from thread-local data. DNSResolver* resolver = get_resolver(); // Spin round until we've finished (successfully or otherwise) or we've done // the maximum number of queries. bool complete = false; bool failed = false; bool server_failed = false; int dns_queries = 0; while ((!complete) && (!failed) && (dns_queries < MAX_DNS_QUERIES)) { // Translate the key into a domain and issue a query for it. std::string domain = key_to_domain(string); struct ares_naptr_reply* naptr_reply = NULL; int status = resolver->perform_naptr_query(domain, naptr_reply, trail); if (status == ARES_SUCCESS) { // Parse the reply into a sorted list of rules. std::vector<Rule> rules; parse_naptr_reply(naptr_reply, rules); // Now spin through the rules, looking for the first match. std::vector<DNSEnumService::Rule>::const_iterator rule; for (rule = rules.begin(); rule != rules.end(); ++rule) { if (rule->matches(string)) { // We found a match, so apply the regular expression to the AUS (not // the previous string - this is what ENUM mandates). If this was a // terminal rule, we now have a SIP URI and we're finished. // Otherwise, the output of the regular expression is used as the // next key. try { string = rule->replace(aus, trail); complete = rule->is_terminal(); } catch(...) // LCOV_EXCL_START Only throws if expression too complex or similar hard-to-hit conditions { TRC_ERROR("Failed to translate number with regex"); failed = true; // LCOV_EXCL_STOP } break; } } // If we didn't find a match (and so hit the end of the list), consider // this a failure. failed = failed || (rule == rules.end()); } else if (status == ARES_ENOTFOUND) { // Our DNS query failed, so give up, but this is not an ENUM server issue - // we just tried to look up an unknown name. failed = true; } else { // Our DNS query failed. Give up, and track an ENUM server failure. failed = true; server_failed = true; } // Free off the NAPTR reply if we have one. if (naptr_reply != NULL) { resolver->free_naptr_reply(naptr_reply); naptr_reply = NULL; } dns_queries++; } // Log that we've finished processing (and whether it was successful or not). if (complete) { TRC_DEBUG("Enum lookup completes: %s", string.c_str()); SAS::Event event(trail, SASEvent::ENUM_COMPLETE, 0); event.add_var_param(user); event.add_var_param(string); SAS::report_event(event); } else { TRC_WARNING("Enum lookup did not complete for user %s", user.c_str()); SAS::Event event(trail, SASEvent::ENUM_INCOMPLETE, 0); event.add_var_param(user); SAS::report_event(event); // On failure, we must return an empty (rather than incomplete) string. string = std::string(""); } // Report state of last communication attempt (which may potentially set/clear // an associated alarm). if (_comm_monitor) { if (server_failed) { _comm_monitor->inform_failure(); } else { _comm_monitor->inform_success(); } } return string; }
/// Update the data for the specified namespace and key. Writes the data /// atomically, so if the underlying data has changed since it was last /// read, the update is rejected and this returns Store::Status::CONTENTION. Store::Status BaseMemcachedStore::set_data(const std::string& table, const std::string& key, const std::string& data, uint64_t cas, int expiry, SAS::TrailId trail) { Store::Status status = Store::Status::OK; TRC_DEBUG("Writing %d bytes to table %s key %s, CAS = %ld, expiry = %d", data.length(), table.c_str(), key.c_str(), cas, expiry); // Construct the fully qualified key. std::string fqkey = table + "\\\\" + key; const char* key_ptr = fqkey.data(); const size_t key_len = fqkey.length(); int vbucket = vbucket_for_key(fqkey); const std::vector<memcached_st*>& replicas = get_replicas(vbucket, Op::WRITE); if (trail != 0) { SAS::Event start(trail, SASEvent::MEMCACHED_SET_START, 0); start.add_var_param(fqkey); start.add_var_param(data); start.add_static_param(cas); start.add_static_param(expiry); SAS::report_event(start); } TRC_DEBUG("%d write replicas for key %s", replicas.size(), fqkey.c_str()); // Calculate a timestamp (least-significant 32 bits of milliseconds since the // epoch) for the current time. We store this in the flags field to allow us // to resolve conflicts when resynchronizing between memcached servers. struct timespec ts; (void)clock_gettime(CLOCK_REALTIME, &ts); uint32_t flags = (uint32_t)((ts.tv_sec * 1000) + (ts.tv_nsec / 1000000)); // Memcached uses a flexible mechanism for specifying expiration. // - 0 indicates never expire. // - <= MEMCACHED_EXPIRATION_MAXDELTA indicates a relative (delta) time. // - > MEMCACHED_EXPIRATION_MAXDELTA indicates an absolute time. // Absolute time is the only way to force immediate expiry. Unfortunately, // it's not reliable - see https://github.com/Metaswitch/cpp-common/issues/160 // for details. Instead, we use relative time for future times (expiry > 0) // and the earliest absolute time for immediate expiry (expiry == 0). time_t memcached_expiration = (time_t)((expiry > 0) ? expiry : MEMCACHED_EXPIRATION_MAXDELTA + 1); // First try to write the primary data record to the first responding // server. memcached_return_t rc = MEMCACHED_ERROR; size_t ii; size_t replica_idx; // If we only have one replica, we should try it twice - // libmemcached won't notice a dropped TCP connection until it tries // to make a request on it, and will fail the request then // reconnect, so the second attempt could still work. size_t attempts = (replicas.size() == 1) ? 2: replicas.size(); for (ii = 0; ii < attempts; ++ii) { if ((replicas.size() == 1) && (ii == 1)) { if (rc != MEMCACHED_CONNECTION_FAILURE) { // This is a legitimate error, not a transient server failure, so we // shouldn't retry. break; } replica_idx = 0; TRC_WARNING("Failed to write to sole memcached replica: retrying once"); } else { replica_idx = ii; } TRC_DEBUG("Attempt conditional write to vbucket %d on replica %d (connection %p), CAS = %ld, expiry = %d", vbucket, replica_idx, replicas[replica_idx], cas, expiry); if (cas == 0) { // New record, so attempt to add (but overwrite any tombstones we // encounter). This will fail if someone else got there first and some // data already exists in memcached for this key. rc = add_overwriting_tombstone(replicas[replica_idx], key_ptr, key_len, vbucket, data, memcached_expiration, flags, trail); } else { // This is an update to an existing record, so use memcached_cas // to make sure it is atomic. rc = memcached_cas_vb(replicas[replica_idx], key_ptr, key_len, _binary ? vbucket : 0, data.data(), data.length(), memcached_expiration, flags, cas); if (!memcached_success(rc)) { TRC_DEBUG("memcached_cas command failed, rc = %d (%s)\n%s", rc, memcached_strerror(replicas[replica_idx], rc), memcached_last_error_message(replicas[replica_idx])); } } if (memcached_success(rc)) { TRC_DEBUG("Conditional write succeeded to replica %d", replica_idx); break; } else if ((rc == MEMCACHED_NOTSTORED) || (rc == MEMCACHED_DATA_EXISTS)) { if (trail != 0) { SAS::Event err(trail, SASEvent::MEMCACHED_SET_CONTENTION, 0); err.add_var_param(fqkey); SAS::report_event(err); } // A NOT_STORED or EXISTS response indicates a concurrent write failure, // so return this to the application immediately - don't go on to // other replicas. TRC_INFO("Contention writing data for %s to store", fqkey.c_str()); status = Store::Status::DATA_CONTENTION; break; } } if ((rc == MEMCACHED_SUCCESS) && (replica_idx < replicas.size())) { // Write has succeeded, so write unconditionally (and asynchronously) // to the replicas. for (size_t jj = replica_idx + 1; jj < replicas.size(); ++jj) { TRC_DEBUG("Attempt unconditional write to replica %d", jj); memcached_behavior_set(replicas[jj], MEMCACHED_BEHAVIOR_NOREPLY, 1); memcached_set_vb(replicas[jj], key_ptr, key_len, _binary ? vbucket : 0, data.data(), data.length(), memcached_expiration, flags); memcached_behavior_set(replicas[jj], MEMCACHED_BEHAVIOR_NOREPLY, 0); } } if ((!memcached_success(rc)) && (rc != MEMCACHED_NOTSTORED) && (rc != MEMCACHED_DATA_EXISTS)) { if (trail != 0) { SAS::Event err(trail, SASEvent::MEMCACHED_SET_FAILED, 0); err.add_var_param(fqkey); SAS::report_event(err); } update_vbucket_comm_state(vbucket, FAILED); if (_comm_monitor) { _comm_monitor->inform_failure(); } TRC_ERROR("Failed to write data for %s to %d replicas", fqkey.c_str(), replicas.size()); status = Store::Status::ERROR; } else { update_vbucket_comm_state(vbucket, OK); if (_comm_monitor) { _comm_monitor->inform_success(); } } return status; }
JSONEnumService::JSONEnumService(std::string configuration) { // Check whether the file exists. struct stat s; if ((stat(configuration.c_str(), &s) != 0) && (errno == ENOENT)) { TRC_STATUS("No ENUM configuration (file %s does not exist)", configuration.c_str()); CL_SPROUT_ENUM_FILE_MISSING.log(configuration.c_str()); return; } TRC_STATUS("Loading ENUM configuration from %s", configuration.c_str()); // Read from the file std::ifstream fs(configuration.c_str()); std::string enum_str((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>()); if (enum_str == "") { // LCOV_EXCL_START TRC_ERROR("Failed to read ENUM configuration data from %s", configuration.c_str()); CL_SPROUT_ENUM_FILE_EMPTY.log(configuration.c_str()); return; // LCOV_EXCL_STOP } // Now parse the document rapidjson::Document doc; doc.Parse<0>(enum_str.c_str()); if (doc.HasParseError()) { TRC_ERROR("Failed to read ENUM configuration data: %s\nError: %s", enum_str.c_str(), rapidjson::GetParseError_En(doc.GetParseError())); CL_SPROUT_ENUM_FILE_INVALID.log(configuration.c_str()); return; } try { JSON_ASSERT_CONTAINS(doc, "number_blocks"); JSON_ASSERT_ARRAY(doc["number_blocks"]); const rapidjson::Value& nb_arr = doc["number_blocks"]; for (rapidjson::Value::ConstValueIterator nb_it = nb_arr.Begin(); nb_it != nb_arr.End(); ++nb_it) { try { std::string prefix; JSON_GET_STRING_MEMBER(*nb_it, "prefix", prefix); std::string regex; JSON_GET_STRING_MEMBER(*nb_it, "regex", regex); // Entry is well-formed, so add it. TRC_DEBUG("Found valid number prefix block %s", prefix.c_str()); NumberPrefix *pfix = new NumberPrefix; pfix->prefix = prefix; if (parse_regex_replace(regex, pfix->match, pfix->replace)) { _number_prefixes.push_back(pfix); TRC_STATUS(" Adding number prefix %s, regex=%s", pfix->prefix.c_str(), regex.c_str()); } else { TRC_WARNING("Badly formed regular expression in ENUM number block %s", regex.c_str()); delete pfix; } } catch (JsonFormatError err) { // Badly formed number block. TRC_WARNING("Badly formed ENUM number block (hit error at %s:%d)", err._file, err._line); CL_SPROUT_ENUM_FILE_INVALID.log(configuration.c_str()); } } } catch (JsonFormatError err) { TRC_ERROR("Badly formed ENUM configuration data - missing number_blocks object"); CL_SPROUT_ENUM_FILE_INVALID.log(configuration.c_str()); } }
HTTPCode BillingTask::parse_body(std::string call_id, bool timer_interim, std::string reqbody, Message** msg, SAS::TrailId trail) { rapidjson::Document* body = new rapidjson::Document(); std::string bodys = reqbody; body->Parse<0>(bodys.c_str()); std::vector<std::string> ccfs; uint32_t session_refresh_time = 0; role_of_node_t role_of_node; node_functionality_t node_functionality; // Log the body early so we still see it if we later determine it's invalid. if (Log::enabled(Log::DEBUG_LEVEL)) { if (body->HasParseError()) { // Print the body from the source string. We can't pretty print an // invalid document. TRC_DEBUG("Handling request, Body:\n%s", reqbody.c_str()); } else { rapidjson::StringBuffer s; rapidjson::PrettyWriter<rapidjson::StringBuffer> w(s); body->Accept(w); TRC_DEBUG("Handling request, body:\n%s", s.GetString()); } } // Verify that the body is correct JSON with an "event" element if (!(*body).IsObject() || !(*body).HasMember("event") || !(*body)["event"].IsObject()) { TRC_WARNING("JSON document was either not valid or did not have an 'event' key"); delete body; return HTTP_BAD_REQUEST; } // Verify the Role-Of-Node and Node-Functionality AVPs are present (we use these // to distinguish devices in path for the same SIP call ID. if ((!(*body)["event"].HasMember("Service-Information")) || (!(*body)["event"]["Service-Information"].IsObject()) || (!(*body)["event"]["Service-Information"].HasMember("IMS-Information")) || (!(*body)["event"]["Service-Information"]["IMS-Information"].IsObject())) { TRC_ERROR("IMS-Information not included in the event description"); delete body; return HTTP_BAD_REQUEST; } else { rapidjson::Value& ims_information_json = (*body)["event"]["Service-Information"]["IMS-Information"]; rapidjson::Value::MemberIterator role_of_node_json = ims_information_json.FindMember("Role-Of-Node"); if ((role_of_node_json == ims_information_json.MemberEnd()) || !(role_of_node_json->value.IsInt())) { TRC_ERROR("No Role-Of-Node in IMS-Information"); delete body; return HTTP_BAD_REQUEST; } role_of_node = (role_of_node_t)role_of_node_json->value.GetInt(); rapidjson::Value::MemberIterator node_function_json = ims_information_json.FindMember("Node-Functionality"); if ((node_function_json == ims_information_json.MemberEnd()) || !(node_function_json->value.IsInt())) { TRC_ERROR("No Node-Functionality in IMS-Information"); delete body; return HTTP_BAD_REQUEST; } node_functionality = (node_functionality_t)node_function_json->value.GetInt(); } // Verify that there is an Accounting-Record-Type and it is one of // the four valid types if (!((*body)["event"].HasMember("Accounting-Record-Type") && ((*body)["event"]["Accounting-Record-Type"].IsInt()))) { TRC_WARNING("Accounting-Record-Type not available in JSON"); delete body; return HTTP_BAD_REQUEST; } Rf::AccountingRecordType record_type((*body)["event"]["Accounting-Record-Type"].GetInt()); if (!record_type.isValid()) { TRC_ERROR("Accounting-Record-Type was not one of START/INTERIM/STOP/EVENT"); delete body; return HTTP_BAD_REQUEST; } // Parsed enough to SAS-log the message. SAS::Event incoming(trail, SASEvent::INCOMING_REQUEST, 0); incoming.add_static_param(record_type.code()); incoming.add_static_param(node_functionality); SAS::report_event(incoming); // Get the Acct-Interim-Interval if present if ((*body)["event"].HasMember("Acct-Interim-Interval") && (*body)["event"]["Acct-Interim-Interval"].IsInt()) { session_refresh_time = (*body)["event"]["Acct-Interim-Interval"].GetInt(); } // If we have a START or EVENT Accounting-Record-Type, we must have // a list of CCFs to use as peers. // If these are missing, Ralf can't send the ACR onto a CDF, but it has // successfully processed the request. Log this and return 200 OK with // no further processing. if (record_type.isStart() || record_type.isEvent()) { if (!((body->HasMember("peers")) && (*body)["peers"].IsObject())) { TRC_ERROR("JSON lacked a 'peers' object (mandatory for START/EVENT)"); SAS::Event missing_peers(trail, SASEvent::INCOMING_REQUEST_NO_PEERS, 0); missing_peers.add_static_param(record_type.code()); SAS::report_event(missing_peers); delete body; return HTTP_OK; } if (!((*body)["peers"].HasMember("ccf")) || !((*body)["peers"]["ccf"].IsArray()) || ((*body)["peers"]["ccf"].Size() == 0)) { TRC_ERROR("JSON lacked a 'ccf' array, or the array was empty (mandatory for START/EVENT)"); delete body; return HTTP_BAD_REQUEST; } for (rapidjson::SizeType i = 0; i < (*body)["peers"]["ccf"].Size(); i++) { if (!(*body)["peers"]["ccf"][i].IsString()) { TRC_ERROR("JSON contains a 'ccf' array but not all the elements are strings"); delete body; return HTTP_BAD_REQUEST; } TRC_DEBUG("Adding CCF %s", (*body)["peers"]["ccf"][i].GetString()); ccfs.push_back((*body)["peers"]["ccf"][i].GetString()); } } *msg = new Message(call_id, role_of_node, node_functionality, body, record_type, session_refresh_time, trail, timer_interim); if (!ccfs.empty()) { (*msg)->ccfs = ccfs; } return HTTP_OK; }