void RegistrationUtils::remove_bindings(RegStore* store, HSSConnection* hss, const std::string& aor, const std::string& binding_id, const std::string& dereg_type, SAS::TrailId trail) { LOG_INFO("Remove binding(s) %s from IMPU %s", binding_id.c_str(), aor.c_str()); if (expire_bindings(store, aor, binding_id, trail)) { // All bindings have been expired, so do deregistration processing for the // IMPU. LOG_INFO("All bindings for %s expired, so deregister at HSS and ASs", aor.c_str()); std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; HTTPCode http_code = hss->update_registration_state(aor, "", dereg_type, ifc_map, uris, trail); if (http_code == HTTP_OK) { // Note that 3GPP TS 24.229 V12.0.0 (2013-03) 5.4.1.7 doesn't specify that any binding information // should be passed on the REGISTER message, so we don't need the binding ID. deregister_with_application_servers(ifc_map[aor], store, aor, trail); notify_application_servers(); } } };
/// Retrieve the registration data for a given SIP Address of Record, creating /// an empty record if no data exists for the AoR. /// /// @param aor_id The SIP Address of Record for the registration RegStore::AoR* RegStore::get_aor_data(const std::string& aor_id, SAS::TrailId trail) { AoR* aor_data = _connector->get_aor_data(aor_id, trail); if (aor_data != NULL) { int now = time(NULL); expire_bindings(aor_data, now, trail); expire_subscriptions(aor_data, now); } return aor_data; }
/// Update the data for a particular address of record. Writes the data /// atomically. If the underlying data has changed since it was last /// read, the update is rejected and this returns false; if the update /// succeeds, this returns true. /// /// If a connection cannot be obtained, returns a random boolean based on /// data found on the call stack at the point of entry. bool MemcachedStore::set_aor_data(const std::string& aor_id, ///< the SIP URI AoR* data) ///< the data to store { memcached_return_t rc; MemcachedAoR* aor_data = (MemcachedAoR*)data; // Try to get a connection. struct timespec wait_time; wait_time.tv_sec = 0; wait_time.tv_nsec = 100 * 1000 * 1000; memcached_st* st = memcached_pool_fetch(_pool, &wait_time, &rc); if (st != NULL) { // Got one: use it. // // Expire any old bindings before writing to the server. In theory, // if there are no bindings left we could delete the entry, but this // may cause concurrency problems because memcached does not support // cas on delete operations. In this case we do a memcached_cas with // an effectively immediate expiry time. int now = time(NULL); int max_expires = expire_bindings(aor_data, now); std::string value = serialize_aor(aor_data); if (aor_data->get_cas() == 0) { // New record, so attempt to add. This will fail if someone else // gets there first. rc = memcached_add(st, aor_id.data(), aor_id.length(), value.data(), value.length(), max_expires, 0); } else { // This is an update to an existing record, so use memcached_cas // to make sure it is atomic. rc = memcached_cas(st, aor_id.data(), aor_id.length(), value.data(), value.length(), max_expires, 0, aor_data->get_cas()); } memcached_pool_release(_pool, st); } return memcached_success(rc); }
/// Retrieve the AoR data for a given SIP URI, creating it if there isn't /// any already, and returning NULL if we can't get a connection. AoR* MemcachedStore::get_aor_data(const std::string& aor_id) ///< the SIP URI { memcached_return_t rc; MemcachedAoR* aor_data = NULL; // Try to get a connection struct timespec wait_time; wait_time.tv_sec = 0; wait_time.tv_nsec = 100 * 1000 * 1000; memcached_st* st = memcached_pool_fetch(_pool, &wait_time, &rc); if (st != NULL) { // Got one: use it. const char* key_ptr = aor_id.data(); const size_t key_len = aor_id.length(); rc = memcached_mget(st, &key_ptr, &key_len, 1); if (memcached_success(rc)) { memcached_result_st result; memcached_result_create(st, &result); memcached_fetch_result(st, &result, &rc); if (memcached_success(rc)) { aor_data = deserialize_aor(std::string(memcached_result_value(&result), memcached_result_length(&result))); aor_data->set_cas(memcached_result_cas(&result)); int now = time(NULL); expire_bindings(aor_data, now); } else { // AoR does not exist, so create it. aor_data = new MemcachedAoR(); } } memcached_pool_release(_pool, st); } return (AoR*)aor_data; }
bool RegStore::set_aor_data(const std::string& aor_id, AoR* aor_data, bool set_chronos, SAS::TrailId trail, bool& all_bindings_expired) { all_bindings_expired = false; // Expire any old bindings before writing to the server. In theory, if // there are no bindings left we could delete the entry, but this may // cause concurrency problems because memcached does not support // cas on delete operations. In this case we do a memcached_cas with // an effectively immediate expiry time. int now = time(NULL); // Set the max expires to be greater than the longest binding expiry time. // This prevents a window condition where Chronos can return a binding to // expire, but memcached has already deleted the aor data (meaning that // no NOTIFYs could be sent) int orig_max_expires = expire_bindings(aor_data, now, trail); int max_expires = orig_max_expires + 10; // expire_bindings returns "now" if there are no remaining bindings, // so test for that. if (orig_max_expires == now) { LOG_DEBUG("All bindings have expired, so this is a deregistration for AOR %s", aor_id.c_str()); all_bindings_expired = true; } // Expire any old subscriptions as well. This doesn't get factored in to // the expiry time on the store record because, according to 5.4.2.1.2 / // TS 24.229, all subscriptions automatically expire when the last binding // expires. expire_subscriptions(aor_data, now); LOG_DEBUG("Set AoR data for %s, CAS=%ld, expiry = %d", aor_id.c_str(), aor_data->_cas, max_expires); // Set the chronos timers if (set_chronos) { for (AoR::Bindings::iterator i = aor_data->_bindings.begin(); i != aor_data->_bindings.end(); ++i) { AoR::Binding* b = i->second; std::string b_id = i->first; HTTPCode status; std::string timer_id = ""; std::string opaque = "{\"aor_id\": \"" + aor_id + "\", \"binding_id\": \"" + b_id +"\"}"; std::string callback_uri = "/timers"; int now = time(NULL); int expiry = b->_expires - now; // If a timer has been previously set for this binding, send a PUT. Otherwise sent a POST. if (b->_timer_id == "") { status = _chronos->send_post(timer_id, expiry, callback_uri, opaque, 0); } else { timer_id = b->_timer_id; status = _chronos->send_put(timer_id, expiry, callback_uri, opaque, 0); } // Update the timer id. If the update to Chronos failed, that's OK, don't reject the register // or update the stored timer id. if (status == HTTP_OK) { b->_timer_id = timer_id; } } } return _connector->set_aor_data(aor_id, aor_data, max_expires - now, trail); }