mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
8.4 KiB
356 lines
8.4 KiB
#ifndef _RegisterCache_h_
|
|
#define _RegisterCache_h_
|
|
|
|
#include "singleton.h"
|
|
#include "hash_table.h"
|
|
#include "atomic_types.h"
|
|
|
|
#include "AmSipMsg.h"
|
|
#include "AmUriParser.h"
|
|
#include "AmAppTimer.h"
|
|
|
|
#include <string>
|
|
#include <map>
|
|
#include <memory>
|
|
using std::string;
|
|
using std::map;
|
|
using std::unique_ptr;
|
|
|
|
#define REG_CACHE_TABLE_POWER 10
|
|
#define REG_CACHE_TABLE_ENTRIES (1<<REG_CACHE_TABLE_POWER)
|
|
|
|
#define DEFAULT_REG_EXPIRES 3600
|
|
|
|
/*
|
|
* Register cache:
|
|
* ---------------
|
|
* Data model:
|
|
* - canonical AoR <--1-to-n--> contacts
|
|
* - alias <--1-to-1--> contact
|
|
*/
|
|
|
|
struct RegBinding
|
|
{
|
|
// Absolute timestamp representing
|
|
// the expiration timer at the
|
|
// registrar side
|
|
long int reg_expire;
|
|
|
|
// unique-id used as contact user toward the registrar
|
|
string alias;
|
|
|
|
RegBinding()
|
|
: reg_expire(0)
|
|
{}
|
|
};
|
|
|
|
// Contact-URI/Public-IP -> RegBinding
|
|
typedef map<string,RegBinding*> AorEntry;
|
|
|
|
struct AliasEntry
|
|
: public DirectAppTimer
|
|
{
|
|
string aor;
|
|
string contact_uri;
|
|
string alias;
|
|
|
|
// saved state for NAT handling
|
|
string source_ip;
|
|
unsigned short source_port;
|
|
string trsp;
|
|
|
|
// sticky interface
|
|
unsigned short local_if;
|
|
|
|
// User-Agent
|
|
string remote_ua;
|
|
|
|
// Absolute timestamp representing
|
|
// the expiration timer at the
|
|
// registered UA side
|
|
long int ua_expire;
|
|
|
|
AliasEntry()
|
|
: source_port(0), local_if(0), ua_expire(0)
|
|
{}
|
|
|
|
// from DirectAppTimer
|
|
void fire();
|
|
};
|
|
|
|
struct RegCacheStorageHandler
|
|
{
|
|
virtual void onDelete(const string& aor, const string& uri,
|
|
const string& alias) {}
|
|
|
|
virtual void onUpdate(const string& canon_aor, const string& alias,
|
|
long int expires, const AliasEntry& alias_update) {}
|
|
|
|
virtual void onUpdate(const string& alias, long int ua_expires) {}
|
|
};
|
|
|
|
/**
|
|
* Registrar/Reg-Caching
|
|
* parsing/processing context
|
|
*/
|
|
struct RegisterCacheCtx
|
|
: public AmObject
|
|
{
|
|
string from_aor;
|
|
bool aor_parsed;
|
|
|
|
vector<AmUriParser> contacts;
|
|
bool contacts_parsed;
|
|
|
|
unsigned int requested_expires;
|
|
bool expires_parsed;
|
|
|
|
unsigned int min_reg_expires;
|
|
unsigned int max_ua_expires;
|
|
|
|
RegisterCacheCtx()
|
|
: aor_parsed(false),
|
|
contacts_parsed(false),
|
|
requested_expires(DEFAULT_REG_EXPIRES),
|
|
expires_parsed(false),
|
|
min_reg_expires(0),
|
|
max_ua_expires(0)
|
|
{}
|
|
};
|
|
|
|
/**
|
|
* Alias hash table:
|
|
* Alias -> Contact-URI
|
|
*/
|
|
class AliasHash
|
|
: public unordered_hash_map<string, AliasEntry*>
|
|
{
|
|
public:
|
|
~AliasHash() {
|
|
for (auto it = begin(); it != end(); it++)
|
|
delete it->second;
|
|
}
|
|
|
|
AliasEntry* getContact(const string& alias);
|
|
|
|
void dump_elmt(const string& alias, AliasEntry* const& ae) const;
|
|
};
|
|
|
|
/**
|
|
* AoR hash table:
|
|
* AoR -> AorEntry
|
|
*/
|
|
class AorHash
|
|
: public unordered_hash_map<string, AorEntry>
|
|
{
|
|
public:
|
|
/* Maintenance stuff */
|
|
|
|
void gbc(long int now, list<string>& alias_list);
|
|
void dump_elmt(const string& aor, const AorEntry& p_aor_entry) const;
|
|
};
|
|
|
|
class ContactKey
|
|
{
|
|
public:
|
|
string uri;
|
|
string ip;
|
|
unsigned short port;
|
|
|
|
ContactKey(string _uri, string _ip, unsigned short _port)
|
|
: uri(_uri),
|
|
ip(_ip),
|
|
port(_port)
|
|
{}
|
|
};
|
|
|
|
template<> struct std::hash<ContactKey> {
|
|
size_t operator()(const ContactKey& k) const {
|
|
return std::hash<string>{}(k.uri) ^ std::hash<string>{}(k.ip) ^ std::hash<unsigned int>{}(k.port);
|
|
}
|
|
};
|
|
template<> struct std::equal_to<ContactKey> {
|
|
size_t operator()(const ContactKey& a, const ContactKey& b) const {
|
|
return a.uri == b.uri
|
|
&& a.ip == b.ip
|
|
&& a.port == b.port;
|
|
}
|
|
};
|
|
|
|
class ContactHash
|
|
: public unordered_hash_map<ContactKey, string>
|
|
{
|
|
public:
|
|
using unordered_hash_map<ContactKey, string>::insert;
|
|
|
|
void insert(const string& contact_uri, const string& remote_ip,
|
|
unsigned short remote_port, const string& alias);
|
|
|
|
string getAlias(const string& contact_uri, const string& remote_ip,
|
|
unsigned short remote_port);
|
|
|
|
void remove(const string& contact_uri, const string& remote_ip,
|
|
unsigned short remote_port);
|
|
|
|
void dump_elmt(const ContactKey& key, const string& alias) const;
|
|
};
|
|
|
|
class _RegisterCache
|
|
: public AmThread
|
|
{
|
|
AorHash reg_cache_ht;
|
|
AliasHash id_idx;
|
|
ContactHash contact_idx;
|
|
|
|
unique_ptr<RegCacheStorageHandler> storage_handler;
|
|
|
|
// stats
|
|
atomic_int active_regs;
|
|
|
|
void gbc();
|
|
void removeAlias(const string& alias, bool generate_event);
|
|
|
|
std::mutex shutdown_mutex;
|
|
std::condition_variable sleep_cond;
|
|
bool shutdown_flag;
|
|
|
|
protected:
|
|
_RegisterCache();
|
|
~_RegisterCache();
|
|
|
|
void dispose() { stop(); }
|
|
|
|
/* AmThread interface */
|
|
void run();
|
|
const char *identify() { return "register cache"; }
|
|
|
|
void on_stop() {
|
|
std::lock_guard<std::mutex> _l(shutdown_mutex);
|
|
shutdown_flag = true;
|
|
sleep_cond.notify_all();
|
|
}
|
|
|
|
int parseAoR(RegisterCacheCtx& ctx, const AmSipRequest& req, msg_logger *logger);
|
|
int parseContacts(RegisterCacheCtx& ctx, const AmSipRequest& req, msg_logger *logger);
|
|
int parseExpires(RegisterCacheCtx& ctx, const AmSipRequest& req, msg_logger *logger);
|
|
|
|
void setAliasUATimer(AliasEntry* alias_e);
|
|
void removeAliasUATimer(AliasEntry* alias_e);
|
|
|
|
public:
|
|
static string canonicalize_aor(const string& aor);
|
|
static string compute_alias_hash(const string& aor, const string& contact_uri,
|
|
const string& public_ip);
|
|
|
|
void setStorageHandler(RegCacheStorageHandler* h) { storage_handler.reset(h); }
|
|
|
|
/**
|
|
* Match, retrieve the contact cache entry associated with the URI passed,
|
|
* and return the alias found in the cache entry.
|
|
*
|
|
* Note: this function locks and unlocks the contact cache bucket.
|
|
*
|
|
* aor: canonical Address-of-Record
|
|
* uri: Contact-URI
|
|
*/
|
|
bool getAlias(const string& aor, const string& uri,
|
|
const string& public_ip, RegBinding& out_binding);
|
|
|
|
/**
|
|
* Update contact cache entry and alias map entries.
|
|
*
|
|
* Note: this function locks and unlocks
|
|
* the contact cache bucket and
|
|
* the alias map bucket.
|
|
*
|
|
* aor: canonical Address-of-Record
|
|
* uri: Contact-URI
|
|
* alias:
|
|
*/
|
|
void update(const string& alias, long int reg_expires,
|
|
const AliasEntry& alias_update);
|
|
|
|
void update(long int reg_expires, const AliasEntry& alias_update);
|
|
|
|
bool updateAliasExpires(const string& alias, long int ua_expires);
|
|
|
|
/**
|
|
* Remove contact cache entry and alias map entries.
|
|
*
|
|
* Note: this function locks and unlocks
|
|
* the contact cache bucket and
|
|
* the alias map bucket.
|
|
*
|
|
* aor: canonical Address-of-Record
|
|
* uri: Contact-URI
|
|
* alias:
|
|
*/
|
|
void remove(const string& aor, const string& uri,
|
|
const string& alias);
|
|
|
|
void remove(const string& aor);
|
|
|
|
/**
|
|
* Retrieve an alias map containing all entries related
|
|
* to a particular AOR. This is needed to support REGISTER
|
|
* with '*' contact.
|
|
*
|
|
* Note: this function locks and unlocks
|
|
* the contact cache bucket.
|
|
*
|
|
* aor: canonical Address-of-Record
|
|
* alias_map: alias -> contact
|
|
*/
|
|
bool getAorAliasMap(const string& aor, map<string,string>& alias_map);
|
|
|
|
/**
|
|
* Retrieve the alias entry related to the given alias
|
|
*/
|
|
bool findAliasEntry(const string& alias, AliasEntry& alias_entry);
|
|
|
|
/**
|
|
* Retrieve the alias entry related to the given contact-URI, remote-IP & port
|
|
*/
|
|
bool findAEByContact(const string& contact_uri, const string& remote_ip,
|
|
unsigned short remote_port, AliasEntry& ae);
|
|
|
|
/**
|
|
* Throttle REGISTER requests
|
|
*
|
|
* Returns false if REGISTER should be forwarded:
|
|
* - if registrar binding should be renewed.
|
|
* - if source IP or port do not match the saved IP & port.
|
|
* - if the request unregisters any contact.
|
|
* - if request is not a REGISTER
|
|
*/
|
|
bool throttleRegister(RegisterCacheCtx& ctx,
|
|
const AmSipRequest& req,
|
|
msg_logger *logger = NULL);
|
|
|
|
/**
|
|
* Save a single REGISTER contact into cache
|
|
*
|
|
* Returns false if failed:
|
|
* - if request is not a REGISTER.
|
|
* - more than one contact should be (un)registered.
|
|
*
|
|
* If true has been returned, the request has already
|
|
* been replied with either an error or 200 (w/ contact).
|
|
*
|
|
* Note: this function also handles binding query.
|
|
* (REGISTER w/o contacts)
|
|
*/
|
|
bool saveSingleContact(RegisterCacheCtx& ctx,
|
|
const AmSipRequest& req,
|
|
msg_logger *logger = NULL);
|
|
|
|
/**
|
|
* Statistics
|
|
*/
|
|
unsigned int getActiveRegs() { return active_regs; }
|
|
};
|
|
|
|
typedef singleton<_RegisterCache> RegisterCache;
|
|
|
|
#endif
|