Example #1
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)
    {
      _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;
}
Example #2
0
// 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;
}
Example #3
0
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);
}
Example #4
0
// 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;
}
Example #5
0
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);
}
Example #7
0
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;
}
Example #8
0
// 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;
}
Example #9
0
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);
    }
  }
}
Example #10
0
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;
}
Example #13
0
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();
  }
}
Example #14
0
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);
}
Example #15
0
/// 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;
}
Example #16
0
/// 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;
}
Example #17
0
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;
}
Example #18
0
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;
}
Example #19
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;
}
Example #20
0
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());
  }
}
Example #21
0
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;
}
Example #22
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(&currenttime);
  tm* ct = localtime(&currenttime);

  // 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);
}
Example #23
0
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;
}
Example #24
0
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);
}
Example #25
0
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();
  }
}
Example #26
0
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);
  }
}
Example #27
0
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;
}
Example #28
0
/// 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;
}
Example #29
0
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());
  }
}
Example #30
0
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;
}