|
@@ -0,0 +1,1751 @@
|
|
|
+/*!
|
|
|
+ * \file
|
|
|
+ * \brief Functions related to dialog creation and searching
|
|
|
+ * \ingroup dialog
|
|
|
+ * Module: \ref dialog
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#include "../../dprint.h"
|
|
|
+#include "../../ut.h"
|
|
|
+#include "../../lib/kmi/mi.h"
|
|
|
+#include "dlg_timer.h"
|
|
|
+#include "dlg_var.h"
|
|
|
+#include "dlg_hash.h"
|
|
|
+#include "dlg_profile.h"
|
|
|
+#include "dlg_handlers.h"
|
|
|
+
|
|
|
+#define MAX_LDG_LOCKS 2048
|
|
|
+#define MIN_LDG_LOCKS 2
|
|
|
+
|
|
|
+
|
|
|
+/*! global dialog table */
|
|
|
+struct dlg_table *d_table = 0;
|
|
|
+/*! global dialog out table */
|
|
|
+static int dlg_hash_size_out = 4096;
|
|
|
+
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Reference a dialog without locking
|
|
|
+ * \param _dlg dialog
|
|
|
+ * \param _cnt increment for the reference counter
|
|
|
+ */
|
|
|
+#define ref_dlg_unsafe(_dlg,_cnt) \
|
|
|
+ do { \
|
|
|
+ (_dlg)->ref += (_cnt); \
|
|
|
+ LM_DBG("ref dlg %p with %d -> %d\n", \
|
|
|
+ (_dlg),(_cnt),(_dlg)->ref); \
|
|
|
+ }while(0)
|
|
|
+
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Unreference a dialog without locking
|
|
|
+ * \param _dlg dialog
|
|
|
+ * \param _cnt decrement for the reference counter
|
|
|
+ * \param _d_entry dialog entry
|
|
|
+ */
|
|
|
+#define unref_dlg_unsafe(_dlg,_cnt,_d_entry) \
|
|
|
+ do { \
|
|
|
+ (_dlg)->ref -= (_cnt); \
|
|
|
+ LM_DBG("unref dlg %p with %d -> %d\n",\
|
|
|
+ (_dlg),(_cnt),(_dlg)->ref);\
|
|
|
+ if ((_dlg)->ref<0) {\
|
|
|
+ LM_CRIT("bogus ref %d with cnt %d for dlg %p [%u:%u] "\
|
|
|
+ "with clid '%.*s' and tags '%.*s'\n",\
|
|
|
+ (_dlg)->ref, _cnt, _dlg,\
|
|
|
+ (_dlg)->h_entry, (_dlg)->h_id,\
|
|
|
+ (_dlg)->callid.len, (_dlg)->callid.s,\
|
|
|
+ (_dlg)->from_tag.len,\
|
|
|
+ (_dlg)->from_tag.s);\
|
|
|
+ }\
|
|
|
+ if ((_dlg)->ref<=0) { \
|
|
|
+ unlink_unsafe_dlg( _d_entry, _dlg);\
|
|
|
+ LM_DBG("ref <=0 for dialog %p\n",_dlg);\
|
|
|
+ destroy_dlg(_dlg);\
|
|
|
+ }\
|
|
|
+ }while(0)
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Initialize the global dialog table
|
|
|
+ * \param size size of the table
|
|
|
+ * \return 0 on success, -1 on failure
|
|
|
+ */
|
|
|
+int init_dlg_table(unsigned int size) {
|
|
|
+ unsigned int n;
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ d_table = (struct dlg_table*) shm_malloc
|
|
|
+ (sizeof (struct dlg_table) +size * sizeof (struct dlg_entry));
|
|
|
+ if (d_table == 0) {
|
|
|
+ LM_ERR("no more shm mem (1)\n");
|
|
|
+ goto error0;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(d_table, 0, sizeof (struct dlg_table));
|
|
|
+ d_table->size = size;
|
|
|
+ d_table->entries = (struct dlg_entry*) (d_table + 1);
|
|
|
+
|
|
|
+ n = (size < MAX_LDG_LOCKS) ? size : MAX_LDG_LOCKS;
|
|
|
+ for (; n >= MIN_LDG_LOCKS; n--) {
|
|
|
+ d_table->locks = lock_set_alloc(n);
|
|
|
+ if (d_table->locks == 0)
|
|
|
+ continue;
|
|
|
+ if (lock_set_init(d_table->locks) == 0) {
|
|
|
+ lock_set_dealloc(d_table->locks);
|
|
|
+ d_table->locks = 0;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ d_table->locks_no = n;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (d_table->locks == 0) {
|
|
|
+ LM_ERR("unable to allocted at least %d locks for the hash table\n",
|
|
|
+ MIN_LDG_LOCKS);
|
|
|
+ goto error1;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < size; i++) {
|
|
|
+ memset(&(d_table->entries[i]), 0, sizeof (struct dlg_entry));
|
|
|
+ d_table->entries[i].next_id = rand();
|
|
|
+ d_table->entries[i].lock_idx = i % d_table->locks_no;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+error1:
|
|
|
+ shm_free(d_table);
|
|
|
+error0:
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief free all the memory in the entry_out structure
|
|
|
+ * \param d_entry_out structure
|
|
|
+ * \return void
|
|
|
+ */
|
|
|
+static void destroy_entry_out(struct dlg_entry_out *d_entry_out) {
|
|
|
+ struct dlg_cell_out *dlg_out;
|
|
|
+ struct dlg_cell_out *dlg_out_tmp;
|
|
|
+ dlg_out = d_entry_out->first;
|
|
|
+
|
|
|
+ LM_DBG("Destroy dialog entry out\n");
|
|
|
+ while (dlg_out) {
|
|
|
+ //clear all dlg_out memory space
|
|
|
+ if (dlg_out->caller_cseq.s) {
|
|
|
+ LM_DBG("content before freeing caller cseq is [%.*s]\n", dlg_out->caller_cseq.len, dlg_out->caller_cseq.s);
|
|
|
+ shm_free(dlg_out->caller_cseq.s);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dlg_out->callee_cseq.s) {
|
|
|
+ LM_DBG("content before freeing callee cseq is [%.*s]\n", dlg_out->callee_cseq.len, dlg_out->callee_cseq.s);
|
|
|
+ shm_free(dlg_out->callee_cseq.s);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dlg_out->callee_contact.s) {
|
|
|
+ LM_DBG("content before freeing callee contact is [%.*s]\n", dlg_out->callee_contact.len, dlg_out->callee_contact.s);
|
|
|
+ shm_free(dlg_out->callee_contact.s);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dlg_out->callee_route_set.s) {
|
|
|
+ shm_free(dlg_out->callee_route_set.s);
|
|
|
+ }
|
|
|
+ if (dlg_out->did.s) {
|
|
|
+ shm_free(dlg_out->did.s);
|
|
|
+ }
|
|
|
+
|
|
|
+ dlg_out_tmp = dlg_out->next;
|
|
|
+ shm_free(dlg_out);
|
|
|
+
|
|
|
+ dlg_out = dlg_out_tmp;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Destroy a dialog, run callbacks and free memory
|
|
|
+ * \param dlg destroyed dialog
|
|
|
+ */
|
|
|
+inline void destroy_dlg(struct dlg_cell *dlg) {
|
|
|
+ int ret = 0;
|
|
|
+ struct dlg_var *var;
|
|
|
+
|
|
|
+ LM_DBG("destroying dialog %p\n", dlg);
|
|
|
+
|
|
|
+ ret = remove_dialog_timer(&dlg->tl);
|
|
|
+ if (ret < 0) {
|
|
|
+ LM_CRIT("unable to unlink the timer on dlg %p [%u:%u] "
|
|
|
+ "with clid '%.*s' and tags '%.*s'\n",
|
|
|
+ dlg, dlg->h_entry, dlg->h_id,
|
|
|
+ dlg->callid.len, dlg->callid.s,
|
|
|
+ dlg->from_tag.len, dlg->from_tag.s);
|
|
|
+
|
|
|
+ } else if (ret > 0) {
|
|
|
+ LM_DBG("removed timer for dlg %p [%u:%u] "
|
|
|
+ "with clid '%.*s' and tags '%.*s' \n",
|
|
|
+ dlg, dlg->h_entry, dlg->h_id,
|
|
|
+ dlg->callid.len, dlg->callid.s,
|
|
|
+ dlg->from_tag.len, dlg->from_tag.s);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("About to run dlg callback for destroy\n");
|
|
|
+ run_dlg_callbacks(DLGCB_DESTROY, dlg, NULL, NULL, DLG_DIR_NONE, 0);
|
|
|
+ LM_DBG("DONE: About to run dlg callback for destroy\n");
|
|
|
+ if (dlg == get_current_dlg_pointer())
|
|
|
+ reset_current_dlg_pointer();
|
|
|
+
|
|
|
+ if (dlg->cbs.first)
|
|
|
+ destroy_dlg_callbacks_list(dlg->cbs.first);
|
|
|
+
|
|
|
+ if (dlg->profile_links)
|
|
|
+ destroy_linkers(dlg->profile_links);
|
|
|
+
|
|
|
+
|
|
|
+ if (dlg->from_tag.s)
|
|
|
+ shm_free(dlg->from_tag.s);
|
|
|
+
|
|
|
+ if (dlg->first_req_cseq.s)
|
|
|
+ shm_free(dlg->first_req_cseq.s);
|
|
|
+
|
|
|
+ if (dlg->toroute_name.s)
|
|
|
+ shm_free(dlg->toroute_name.s);
|
|
|
+
|
|
|
+ if (dlg->did.s)
|
|
|
+ shm_free(dlg->did.s);
|
|
|
+
|
|
|
+ if (dlg->caller_route_set.s)
|
|
|
+ shm_free(dlg->caller_route_set.s);
|
|
|
+
|
|
|
+ if (dlg->caller_contact.s)
|
|
|
+ shm_free(dlg->caller_contact.s);
|
|
|
+
|
|
|
+ while (dlg->vars) {
|
|
|
+
|
|
|
+ var = dlg->vars;
|
|
|
+ dlg->vars = dlg->vars->next;
|
|
|
+ shm_free(var->key.s);
|
|
|
+ shm_free(var->value.s);
|
|
|
+ shm_free(var);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (&(dlg->dlg_entry_out)) {
|
|
|
+ lock_get(dlg->dlg_out_entries_lock);
|
|
|
+ destroy_entry_out(&(dlg->dlg_entry_out));
|
|
|
+ lock_release(dlg->dlg_out_entries_lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ lock_destroy(dlg->dlg_out_entries_lock);
|
|
|
+ lock_dealloc(dlg->dlg_out_entries_lock);
|
|
|
+
|
|
|
+ shm_free(dlg);
|
|
|
+
|
|
|
+ dlg = 0;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Destroy the global dialog table
|
|
|
+ */
|
|
|
+void destroy_dlg_table(void) {
|
|
|
+ struct dlg_cell *dlg, *l_dlg;
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ if (d_table == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (d_table->locks) {
|
|
|
+ lock_set_destroy(d_table->locks);
|
|
|
+ lock_set_dealloc(d_table->locks);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < d_table->size; i++) {
|
|
|
+ dlg = d_table->entries[i].first;
|
|
|
+ while (dlg) {
|
|
|
+ l_dlg = dlg;
|
|
|
+ dlg = dlg->next;
|
|
|
+ destroy_dlg(l_dlg);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ shm_free(d_table);
|
|
|
+ d_table = 0;
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Create a new dialog structure for a SIP dialog
|
|
|
+ * \param callid dialog callid
|
|
|
+ * \param from_uri dialog from uri
|
|
|
+ * \param to_uri dialog to uri
|
|
|
+ * \param from_tag dialog from tag
|
|
|
+ * \param req_uri dialog r-uri
|
|
|
+ * \return created dialog structure on success, NULL otherwise
|
|
|
+ */
|
|
|
+struct dlg_cell* build_new_dlg(str *callid, str *from_uri, str *from_tag, str *req_uri) {
|
|
|
+ struct dlg_cell *dlg;
|
|
|
+ int len;
|
|
|
+ char *p;
|
|
|
+
|
|
|
+ len = sizeof (struct dlg_cell) +callid->len + from_uri->len + req_uri->len;
|
|
|
+ dlg = (struct dlg_cell*) shm_malloc(len);
|
|
|
+ if (dlg == 0) {
|
|
|
+ LM_ERR("no more shm mem (%d)\n", len);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(dlg, 0, len);
|
|
|
+
|
|
|
+ dlg->dlg_out_entries_lock = lock_alloc();
|
|
|
+ if (dlg->dlg_out_entries_lock == NULL) {
|
|
|
+ LM_ERR("Cannot allocate lock for dlg out entries. Aborting...\n");
|
|
|
+ shm_free(dlg);
|
|
|
+ return 0;
|
|
|
+ } else {
|
|
|
+ if (lock_init(dlg->dlg_out_entries_lock) == NULL) {
|
|
|
+ LM_ERR("Cannot init the lock for dlg out entries. Aborting...\n");
|
|
|
+ lock_destroy(dlg->dlg_out_entries_lock);
|
|
|
+ lock_dealloc(dlg->dlg_out_entries_lock);
|
|
|
+ shm_free(dlg);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dlg->state = DLG_STATE_UNCONFIRMED;
|
|
|
+
|
|
|
+ dlg->h_entry = core_hash(callid, 0, d_table->size);
|
|
|
+ LM_DBG("new dialog on hash %u\n", dlg->h_entry);
|
|
|
+
|
|
|
+ p = (char*) (dlg + 1);
|
|
|
+
|
|
|
+ dlg->callid.s = p;
|
|
|
+ dlg->callid.len = callid->len;
|
|
|
+ memcpy(p, callid->s, callid->len);
|
|
|
+ p += callid->len;
|
|
|
+
|
|
|
+ dlg->from_uri.s = p;
|
|
|
+ dlg->from_uri.len = from_uri->len;
|
|
|
+ memcpy(p, from_uri->s, from_uri->len);
|
|
|
+ p += from_uri->len;
|
|
|
+
|
|
|
+ dlg->req_uri.s = p;
|
|
|
+ dlg->req_uri.len = req_uri->len;
|
|
|
+ memcpy(p, req_uri->s, req_uri->len);
|
|
|
+ p += req_uri->len;
|
|
|
+
|
|
|
+ if (p != (((char*) dlg) + len)) {
|
|
|
+ LM_CRIT("buffer overflow\n");
|
|
|
+ shm_free(dlg);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return dlg;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Set the leg information for an existing dialog
|
|
|
+ * \param dlg dialog
|
|
|
+ * \param tag from tag or to tag
|
|
|
+ * \param rr record-routing information
|
|
|
+ * \param contact caller or callee contact
|
|
|
+ * \param cseq CSEQ of caller or callee
|
|
|
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
|
|
|
+ * \return 0 on success, -1 on failure
|
|
|
+ */
|
|
|
+int dlg_set_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,
|
|
|
+ str *cseq, struct socket_info *bind_addr, unsigned int leg) {
|
|
|
+
|
|
|
+ if (!dlg) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ //create new dlg_out entry
|
|
|
+ struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
|
|
|
+ struct dlg_cell_out *dlg_out;
|
|
|
+
|
|
|
+ if (leg == DLG_CALLER_LEG) {
|
|
|
+ if (tag->len > 0) {
|
|
|
+ dlg->from_tag.s = (char*) shm_malloc(tag->len);
|
|
|
+ if (!dlg->from_tag.s) {
|
|
|
+ LM_ERR("no more shm_mem\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ memcpy(dlg->from_tag.s, tag->s, tag->len);
|
|
|
+ dlg->from_tag.len = tag->len;
|
|
|
+ }
|
|
|
+ if (contact->len > 0) {
|
|
|
+ dlg->caller_contact.s = (char*) shm_malloc(contact->len);
|
|
|
+ if (!dlg->caller_contact.s) {
|
|
|
+ LM_ERR("no more shm_mem\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ memcpy(dlg->caller_contact.s, contact->s, contact->len);
|
|
|
+ dlg->caller_contact.len = contact->len;
|
|
|
+ }
|
|
|
+ if (rr->len > 0) {
|
|
|
+ dlg->caller_route_set.s = (char*) shm_malloc(rr->len);
|
|
|
+ if (!dlg->caller_route_set.s) {
|
|
|
+ LM_ERR("no more shm_mem\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ memcpy(dlg->caller_route_set.s, rr->s, rr->len);
|
|
|
+ dlg->caller_route_set.len = rr->len;
|
|
|
+ }
|
|
|
+ if (cseq->len > 0) {
|
|
|
+ dlg->first_req_cseq.s = (char*) shm_malloc(cseq->len);
|
|
|
+ if (!dlg->first_req_cseq.s) {
|
|
|
+ LM_ERR("no more shm_mem\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ memcpy(dlg->first_req_cseq.s, cseq->s, cseq->len);
|
|
|
+ dlg->first_req_cseq.len = cseq->len;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* this is the callee side so we need to find the dialog_out entry with the correct to_tag
|
|
|
+ and assign caller and callee cseq, callee contact, callee route_set
|
|
|
+ */
|
|
|
+ if (d_entry_out) {
|
|
|
+ lock_get(dlg->dlg_out_entries_lock);
|
|
|
+ dlg_out = d_entry_out->first;
|
|
|
+ while (dlg_out) {
|
|
|
+ LM_DBG("Searching out dialog with to_tag '%.*s' (looking for %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s, tag->len, tag->s);
|
|
|
+
|
|
|
+ if (dlg_out->to_tag.len == tag->len && memcmp(dlg_out->to_tag.s, tag->s, dlg_out->to_tag.len) == 0) {
|
|
|
+ LM_ERR("Found dialog_out entry with correct to_tag - updating leg info\n");
|
|
|
+
|
|
|
+ if (contact->len > 0) {
|
|
|
+ dlg_out->callee_contact.s = (char*) shm_malloc(contact->len);
|
|
|
+ if (!dlg_out->callee_contact.s) {
|
|
|
+ LM_ERR("no more shm mem\n");
|
|
|
+ return -1; //if we're out of mem we dont really care about cleaning up - prob going to crash anyway
|
|
|
+ }
|
|
|
+ dlg_out->callee_contact.len = contact->len;
|
|
|
+ memcpy(dlg_out->callee_contact.s, contact->s, contact->len);
|
|
|
+ }
|
|
|
+ if (rr->len > 0) {
|
|
|
+ dlg_out->callee_route_set.s = (char*) shm_malloc(rr->len);
|
|
|
+ if (!dlg_out->callee_route_set.s) {
|
|
|
+ LM_ERR("no more shm mem\n");
|
|
|
+ return -1; //if we're out of mem we dont really care about cleaning up - prob going to crash anyway
|
|
|
+ }
|
|
|
+ dlg_out->callee_route_set.len = rr->len;
|
|
|
+ memcpy(dlg_out->callee_route_set.s, rr->s, rr->len);
|
|
|
+ }
|
|
|
+ if (cseq->len > 0) {
|
|
|
+ dlg_out->callee_cseq.s = (char*) shm_malloc(cseq->len);
|
|
|
+ dlg_out->caller_cseq.s = (char*) shm_malloc(cseq->len);
|
|
|
+ if (!dlg_out->callee_cseq.s || !dlg_out->caller_cseq.s) {
|
|
|
+ LM_ERR("no more shm mem\n");
|
|
|
+ return -1; //if we're out of mem we dont really care about cleaning up - prob going to crash anyway
|
|
|
+ }
|
|
|
+ dlg_out->caller_cseq.len = cseq->len;
|
|
|
+ memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len);
|
|
|
+ dlg_out->callee_cseq.len = cseq->len;
|
|
|
+ memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len);
|
|
|
+ }
|
|
|
+ dlg_out->callee_bind_addr = bind_addr;
|
|
|
+ }
|
|
|
+ dlg_out = dlg_out->next;
|
|
|
+ }
|
|
|
+ lock_release(dlg->dlg_out_entries_lock);
|
|
|
+ } else {
|
|
|
+ LM_ERR("This dialog has no dialog out entries\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Create a new dialog out structure for a SIP dialog
|
|
|
+ * \param to_tag - dialog to_tag
|
|
|
+ * \return created dlg_out structure on success, NULL otherwise
|
|
|
+ */
|
|
|
+struct dlg_cell_out* build_new_dlg_out(struct dlg_cell *dlg, str* to_uri, str* to_tag) {
|
|
|
+
|
|
|
+ struct dlg_cell_out *dlg_out;
|
|
|
+ int len;
|
|
|
+ char *p;
|
|
|
+
|
|
|
+ //len = sizeof (struct dlg_cell_out) +dlg->did.len + to_tag->len + to_uri->len;
|
|
|
+ len = sizeof (struct dlg_cell_out) +to_tag->len + to_uri->len;
|
|
|
+
|
|
|
+ dlg_out = (struct dlg_cell_out*) shm_malloc(len);
|
|
|
+ if (dlg_out == 0) {
|
|
|
+ LM_ERR("no more shm mem (%d)\n", len);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ memset(dlg_out, 0, len);
|
|
|
+
|
|
|
+ dlg_out->h_entry = core_hash(to_tag, 0, dlg_hash_size_out);
|
|
|
+ LM_DBG("new dialog_out on hash %u\n", dlg_out->h_entry);
|
|
|
+
|
|
|
+ p = (char*) (dlg_out + 1);
|
|
|
+
|
|
|
+ //dlg_out->did.s = p;
|
|
|
+ //dlg_out->did.len = dlg->did.len;
|
|
|
+ //memcpy(p, dlg->did.s, dlg->did.len);
|
|
|
+ //p += dlg->did.len;
|
|
|
+
|
|
|
+ dlg_out->to_uri.s = p;
|
|
|
+ dlg_out->to_uri.len = to_uri->len;
|
|
|
+ memcpy(p, to_uri->s, to_uri->len);
|
|
|
+ p += to_uri->len;
|
|
|
+
|
|
|
+ dlg_out->to_tag.s = p;
|
|
|
+ dlg_out->to_tag.len = to_tag->len;
|
|
|
+ memcpy(p, to_tag->s, to_tag->len);
|
|
|
+ p += to_tag->len;
|
|
|
+
|
|
|
+ if (p != (((char*) dlg_out) + len)) {
|
|
|
+ LM_CRIT("buffer overflow\n");
|
|
|
+ shm_free(dlg_out);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ //did might be updated (check update_did_dlg_out) if there is a concurrent call -therefore this should not be done as single block of memory
|
|
|
+ //so Richard editted this to not have did in the single block of memory
|
|
|
+ if (dlg->did.len > 0) {
|
|
|
+ dlg_out->did.s = (char*) shm_malloc(dlg->did.len);
|
|
|
+ if (!dlg_out->did.s) {
|
|
|
+ LM_ERR("no more shm_mem\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ memcpy(dlg_out->did.s, dlg->did.s, dlg->did.len);
|
|
|
+ dlg_out->did.len = dlg->did.len;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return dlg_out;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Free the memory for a dlg_out cell
|
|
|
+ * \param dlg_out structure
|
|
|
+ * \return void
|
|
|
+ */
|
|
|
+void free_dlg_out_cell(struct dlg_cell_out *dlg_out) {
|
|
|
+ if (dlg_out->callee_contact.s)
|
|
|
+ shm_free(dlg_out->callee_contact.s);
|
|
|
+ if (dlg_out->callee_cseq.s)
|
|
|
+ shm_free(dlg_out->callee_cseq.s);
|
|
|
+ if (dlg_out->callee_route_set.s)
|
|
|
+ shm_free(dlg_out->callee_route_set.s);
|
|
|
+ if (dlg_out->caller_cseq.s)
|
|
|
+ shm_free(dlg_out->caller_cseq.s);
|
|
|
+
|
|
|
+ //Richard removed this - it is free-ed two lines above!!??!
|
|
|
+ //if (dlg_out->callee_route_set.s)
|
|
|
+ // shm_free(dlg_out->callee_route_set.s);
|
|
|
+
|
|
|
+ //Richard added this as the did is now malloc-ed separately and not as a single concurrent block (as the totag etc. are)
|
|
|
+ if (dlg_out->did.s)
|
|
|
+ shm_free(dlg_out->did.s);
|
|
|
+
|
|
|
+
|
|
|
+ shm_free(dlg_out);
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Remove dlg_out entry identified by to_tag from dlg structure
|
|
|
+ * \param dlg structure
|
|
|
+ * \param dlg_out to_tag
|
|
|
+ * \return void
|
|
|
+ */
|
|
|
+void dlg_remove_dlg_out_tag(struct dlg_cell *dlg, str *to_tag) {
|
|
|
+
|
|
|
+ lock_get(dlg->dlg_out_entries_lock);
|
|
|
+ struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
|
|
|
+ struct dlg_cell_out *pdlg_out = d_entry_out->first;
|
|
|
+ struct dlg_cell_out *tmpdlg = 0;
|
|
|
+
|
|
|
+ int only = 0;
|
|
|
+
|
|
|
+ while (pdlg_out) {
|
|
|
+ if (pdlg_out->deleted) {
|
|
|
+ LM_DBG("Found dlg_out to remove\n");
|
|
|
+ if (pdlg_out->prev) {
|
|
|
+ pdlg_out->prev->next = pdlg_out->next;
|
|
|
+ } else {
|
|
|
+ //assume that we are first
|
|
|
+ if (pdlg_out->next) {
|
|
|
+ d_entry_out->first = pdlg_out->next;
|
|
|
+ pdlg_out->next->prev = 0;
|
|
|
+ } else {
|
|
|
+ LM_ERR("dlg out entry has prev set to null and next set to null too\n");
|
|
|
+ only = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (pdlg_out->next) {
|
|
|
+ pdlg_out->next->prev = pdlg_out->prev;
|
|
|
+ } else {
|
|
|
+ //we are likely the last
|
|
|
+ if (pdlg_out->prev) {
|
|
|
+ d_entry_out->last = pdlg_out->prev;
|
|
|
+ } else {
|
|
|
+ LM_ERR("dlg out next is NULL and so is prev");
|
|
|
+ only = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ tmpdlg = pdlg_out->next;
|
|
|
+ free_dlg_out_cell(pdlg_out);
|
|
|
+ pdlg_out = tmpdlg;
|
|
|
+
|
|
|
+ if (only) {
|
|
|
+ d_entry_out->last = 0;
|
|
|
+ d_entry_out->first = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ LM_DBG("Not deleting dlg_out as it is not set to deleted\n");
|
|
|
+ pdlg_out = pdlg_out->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ lock_release(dlg->dlg_out_entries_lock);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Remove all dlg_out entries from dlg structure expect that identified as dlg_do_not_remove
|
|
|
+ * \param dlg_out cell - structure to not remove
|
|
|
+ * \param mark_only - 1 then only mark for deletion, if 0 then delete
|
|
|
+ * \return void
|
|
|
+ */
|
|
|
+
|
|
|
+void dlg_remove_dlg_out(struct dlg_cell_out *dlg_out_do_not_remove, struct dlg_cell *dlg, int only_mark) {
|
|
|
+
|
|
|
+ lock_get(dlg->dlg_out_entries_lock);
|
|
|
+ //get dlg_out_entry list from dlg
|
|
|
+ struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
|
|
|
+ struct dlg_cell_out *dlg_out;
|
|
|
+
|
|
|
+ //check if list is empty
|
|
|
+ if ((d_entry_out->first == d_entry_out->last) && (d_entry_out->first == 0)) {
|
|
|
+ LM_DBG("There are no dlg_out entries\n");
|
|
|
+ lock_release(dlg->dlg_out_entries_lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ dlg_out = d_entry_out->first;
|
|
|
+ LM_DBG("Scanning dlg_entry_out list for dlg_out entry with did: [%s]", dlg->did.s);
|
|
|
+ //run through the list and for each dlg_out_entry:
|
|
|
+ while (dlg_out) {
|
|
|
+
|
|
|
+ //check if it is the dlg_out that we don't want to remove (compare the to-tags)
|
|
|
+ if (dlg_out->to_tag.len == dlg_out_do_not_remove->to_tag.len &&
|
|
|
+ memcmp(dlg_out->to_tag.s, dlg_out_do_not_remove->to_tag.s, dlg_out->to_tag.len) == 0) {
|
|
|
+ LM_DBG("This is the dlg_out not to be removed!\n");
|
|
|
+ } else {
|
|
|
+ //check if this the last entry in the entry_table
|
|
|
+ if ((d_entry_out->first == d_entry_out->last)) {
|
|
|
+ //we shouldnt ever get here
|
|
|
+ LM_DBG("This is the last dlg_out_entry in the dlg_entries_out\n");
|
|
|
+ //this is the last then set entry_out-> first and entry_out->last to zero
|
|
|
+ dlg->dlg_entry_out.first = dlg->dlg_entry_out.last = 0;
|
|
|
+ } else {
|
|
|
+ if (!only_mark) {
|
|
|
+ LM_DBG("Deleteing dlg out structure\n");
|
|
|
+ if (dlg_out->prev) {
|
|
|
+ dlg_out->prev->next = dlg_out->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ //make the next->previous dlg_out point to the previous of this dlg_out
|
|
|
+ //do not do this if this is the last entry in the list as the next struct is null
|
|
|
+ if (dlg_out->next) {
|
|
|
+ dlg_out->next->prev = dlg_out->prev;
|
|
|
+ }
|
|
|
+
|
|
|
+ free_dlg_out_cell(dlg_out);
|
|
|
+ } else {
|
|
|
+ LM_DBG("Marking dlg_out structure for deletion - it should be deleted by tm callback instead to_tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s);
|
|
|
+ dlg_out->deleted = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dlg_out = dlg_out->next;
|
|
|
+ }
|
|
|
+ lock_release(dlg->dlg_out_entries_lock);
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Update or set the CSEQ for an existing dialog
|
|
|
+ * \param dlg dialog
|
|
|
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
|
|
|
+ * \param cseq CSEQ of caller or callee
|
|
|
+ * \return 0 on success, -1 on failure
|
|
|
+ */
|
|
|
+int dlg_update_cseq(struct dlg_cell * dlg, unsigned int leg, str *cseq, str *to_tag) {
|
|
|
+ LM_DBG("trying to update cseq with seq [%.*s]\n", cseq->len, cseq->s);
|
|
|
+
|
|
|
+
|
|
|
+ //Runs through the dlg_oput entries finds the one that matches the to_tag and updates the callee or caller cseq accordingly
|
|
|
+ struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
|
|
|
+ struct dlg_cell_out *dlg_out;
|
|
|
+ dlg_out = d_entry_out->first;
|
|
|
+ if (to_tag) {
|
|
|
+
|
|
|
+ //compare the to_tag passed parameter to all the dlg_out to_tag entry of the dlg parameter (There could be multiple)
|
|
|
+ while (dlg_out) {
|
|
|
+
|
|
|
+ if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) {
|
|
|
+ //this parameter matches we have found the dlg_out to update the cseq
|
|
|
+
|
|
|
+ if (leg == DLG_CALLER_LEG) {
|
|
|
+ //update caller cseq
|
|
|
+ if (dlg_out->caller_cseq.s) {
|
|
|
+ if (dlg_out->caller_cseq.len < cseq->len) {
|
|
|
+ shm_free(dlg_out->caller_cseq.s);
|
|
|
+ dlg_out->caller_cseq.s = (char*) shm_malloc(cseq->len);
|
|
|
+ if (dlg_out->caller_cseq.s == NULL)
|
|
|
+ goto error;
|
|
|
+ dlg_out->caller_cseq.len = cseq->len;
|
|
|
+ memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dlg_out->caller_cseq.s = (char*) shm_malloc(cseq->len);
|
|
|
+ if (dlg_out->caller_cseq.s == NULL)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ dlg_out->caller_cseq.len = cseq->len;
|
|
|
+ memcpy(dlg_out->caller_cseq.s, cseq->s, cseq->len);
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (leg == DLG_CALLEE_LEG) {
|
|
|
+ //update callee cseq
|
|
|
+ if (dlg_out->callee_cseq.s) {
|
|
|
+ if (dlg_out->callee_cseq.len < cseq->len) {
|
|
|
+ shm_free(dlg_out->callee_cseq.s);
|
|
|
+ dlg_out->callee_cseq.s = (char*) shm_malloc(cseq->len);
|
|
|
+ if (dlg_out->callee_cseq.s == NULL)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ dlg_out->callee_cseq.len = cseq->len;
|
|
|
+ memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dlg_out->callee_cseq.s = (char*) shm_malloc(cseq->len);
|
|
|
+ if (dlg_out->callee_cseq.s == NULL)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ dlg_out->callee_cseq.len = cseq->len;
|
|
|
+ memcpy(dlg_out->callee_cseq.s, cseq->s, cseq->len);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dlg_out = dlg_out->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+error:
|
|
|
+ LM_ERR("not more shm mem\n");
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Update or set the CSEQ for an existing dialog
|
|
|
+ * \param dlg dialog
|
|
|
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
|
|
|
+ * \param cseq CSEQ of caller or callee
|
|
|
+ * \return 0 on success, -1 on failure
|
|
|
+ */
|
|
|
+int dlg_update_contact(struct dlg_cell * dlg, unsigned int leg, str *contact, str *to_tag) {
|
|
|
+ LM_DBG("trying to update contact with contact [%.*s]\n", contact->len, contact->s);
|
|
|
+
|
|
|
+ //Runs through the dlg_oput entries finds the one that matches the to_tag and updates the callee or caller contact accordingly
|
|
|
+ struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
|
|
|
+ struct dlg_cell_out *dlg_out;
|
|
|
+ dlg_out = d_entry_out->first;
|
|
|
+
|
|
|
+ if (leg == DLG_CALLER_LEG) {
|
|
|
+ //update caller contact
|
|
|
+ if (dlg->caller_contact.s) {
|
|
|
+ if (dlg->caller_contact.len < contact->len) {
|
|
|
+ shm_free(dlg->caller_contact.s);
|
|
|
+ dlg->caller_contact.s = (char*) shm_malloc(contact->len);
|
|
|
+ if (dlg->caller_contact.s == NULL)
|
|
|
+ goto error;
|
|
|
+ dlg->caller_contact.len = contact->len;
|
|
|
+ memcpy(dlg->caller_contact.s, contact->s, contact->len);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dlg->caller_contact.s = (char*) shm_malloc(contact->len);
|
|
|
+ if (dlg->caller_contact.s == NULL)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ dlg->caller_contact.len = contact->len;
|
|
|
+ memcpy(dlg->caller_contact.s, contact->s, contact->len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (leg == DLG_CALLEE_LEG) {
|
|
|
+ //update callee contact
|
|
|
+ if (!to_tag) {
|
|
|
+ LM_ERR("No to tag to identify dlg_out\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ //compare the to_tag passed parameter to all the dlg_out to_tag entry of the dlg parameter (There could be multiple)
|
|
|
+ while (dlg_out) {
|
|
|
+
|
|
|
+ if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) {
|
|
|
+ //this parameter matches we have found the dlg_out to update the callee contact
|
|
|
+
|
|
|
+ //update callee contact
|
|
|
+ if (dlg_out->callee_contact.s) {
|
|
|
+ if (dlg_out->callee_contact.len < contact->len) {
|
|
|
+ shm_free(dlg_out->callee_contact.s);
|
|
|
+ dlg_out->callee_contact.s = (char*) shm_malloc(contact->len);
|
|
|
+ if (dlg_out->callee_contact.s == NULL)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ dlg_out->callee_contact.len = contact->len;
|
|
|
+ memcpy(dlg_out->callee_contact.s, contact->s, contact->len);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dlg_out->callee_contact.s = (char*) shm_malloc(contact->len);
|
|
|
+ if (dlg_out->callee_contact.s == NULL)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ dlg_out->callee_contact.len = contact->len;
|
|
|
+ memcpy(dlg_out->callee_contact.s, contact->s, contact->len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dlg_out = dlg_out->next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+error:
|
|
|
+ LM_ERR("not more shm mem\n");
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Lookup a dialog in the global list
|
|
|
+ *
|
|
|
+ * Note that the caller is responsible for decrementing (or reusing)
|
|
|
+ * the reference counter by one again iff a dialog has been found.
|
|
|
+ * \param h_entry number of the hash table entry
|
|
|
+ * \param h_id id of the hash table entry
|
|
|
+ * \return dialog structure on success, NULL on failure
|
|
|
+ */
|
|
|
+struct dlg_cell * lookup_dlg(unsigned int h_entry, unsigned int h_id) {
|
|
|
+ struct dlg_cell *dlg;
|
|
|
+ struct dlg_entry *d_entry;
|
|
|
+
|
|
|
+ if (h_entry >= d_table->size)
|
|
|
+ goto not_found;
|
|
|
+
|
|
|
+ d_entry = &(d_table->entries[h_entry]);
|
|
|
+
|
|
|
+ dlg_lock(d_table, d_entry);
|
|
|
+
|
|
|
+ for (dlg = d_entry->first; dlg; dlg = dlg->next) {
|
|
|
+ if (dlg->h_id == h_id) {
|
|
|
+ ref_dlg_unsafe(dlg, 1);
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+ LM_DBG("dialog id=%u found on entry %u\n", h_id, h_entry);
|
|
|
+ return dlg;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+not_found:
|
|
|
+ LM_DBG("no dialog id=%u found on entry %u\n", h_id, h_entry);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Helper function to get a dialog corresponding to a SIP message
|
|
|
+ * \see get_dlg
|
|
|
+ * \param callid callid
|
|
|
+ * \param ftag from tag
|
|
|
+ * \param ttag to tag
|
|
|
+ * \param dir direction
|
|
|
+ * \return dialog structure on success, NULL on failure
|
|
|
+ */
|
|
|
+static inline struct dlg_cell * internal_get_dlg(unsigned int h_entry,
|
|
|
+ str *callid, str *ftag, str *ttag, unsigned int *dir) {
|
|
|
+ struct dlg_cell *dlg;
|
|
|
+ struct dlg_entry *d_entry;
|
|
|
+
|
|
|
+ d_entry = &(d_table->entries[h_entry]);
|
|
|
+
|
|
|
+ dlg_lock(d_table, d_entry);
|
|
|
+
|
|
|
+ for (dlg = d_entry->first; dlg; dlg = dlg->next) {
|
|
|
+ /* Check callid / fromtag / totag */
|
|
|
+ if (match_dialog(dlg, callid, ftag, ttag, dir) == 1) {
|
|
|
+ ref_dlg_unsafe(dlg, 1);
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+ return dlg;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Get dialog that correspond to CallId, From Tag and To Tag
|
|
|
+ *
|
|
|
+ * Get dialog that correspond to CallId, From Tag and To Tag.
|
|
|
+ * See RFC 3261, paragraph 4. Overview of Operation:
|
|
|
+ * "The combination of the To tag, From tag, and Call-ID completely
|
|
|
+ * defines a peer-to-peer SIP relationship between [two UAs] and is
|
|
|
+ * referred to as a dialog."
|
|
|
+ * Note that the caller is responsible for decrementing (or reusing)
|
|
|
+ * the reference counter by one again iff a dialog has been found.
|
|
|
+ * \param callid callid
|
|
|
+ * \param ftag from tag
|
|
|
+ * \param ttag to tag
|
|
|
+ * \param dir direction
|
|
|
+ * \return dialog structure on success, NULL on failure
|
|
|
+ */
|
|
|
+struct dlg_cell * get_dlg(str *callid, str *ftag, str *ttag, unsigned int *dir) {
|
|
|
+ struct dlg_cell *dlg;
|
|
|
+
|
|
|
+ if ((dlg = internal_get_dlg(core_hash(callid, 0,
|
|
|
+ d_table->size), callid, ftag, ttag, dir)) == 0 &&
|
|
|
+ (dlg = internal_get_dlg(core_hash(callid, ttag->len
|
|
|
+ ? ttag : 0, d_table->size), callid, ftag, ttag, dir)) == 0) {
|
|
|
+ LM_DBG("no dialog callid='%.*s' found\n", callid->len, callid->s);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return dlg;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Link a dialog structure
|
|
|
+ * \param dlg dialog
|
|
|
+ * \param n extra increments for the reference counter
|
|
|
+ */
|
|
|
+void link_dlg_out(struct dlg_cell *dlg, struct dlg_cell_out *dlg_out, int n) {
|
|
|
+ LM_DBG("Start: link_dlg_out\n");
|
|
|
+
|
|
|
+ lock_get(dlg->dlg_out_entries_lock);
|
|
|
+ struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
|
|
|
+
|
|
|
+ if ((d_entry_out->first == d_entry_out->last) && (d_entry_out->first == 0)) {
|
|
|
+ //adding first out dialog
|
|
|
+ LM_DBG("Adding first dlg_out structure\n");
|
|
|
+ d_entry_out->first = dlg_out;
|
|
|
+ d_entry_out->last = dlg_out;
|
|
|
+ } else {
|
|
|
+ LM_DBG("Adding new dlg_out structure\n");
|
|
|
+ dlg_out->prev = d_entry_out->last;
|
|
|
+ dlg_out->next = 0;
|
|
|
+
|
|
|
+ d_entry_out->last->next = dlg_out;
|
|
|
+ d_entry_out->last = dlg_out;
|
|
|
+ }
|
|
|
+
|
|
|
+ lock_release(dlg->dlg_out_entries_lock);
|
|
|
+
|
|
|
+ LM_DBG("Done: link_dlg_out\n");
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Link a dialog structure
|
|
|
+ * \param dlg dialog
|
|
|
+ * \param n extra increments for the reference counter
|
|
|
+ */
|
|
|
+void link_dlg(struct dlg_cell *dlg, int n) {
|
|
|
+ struct dlg_entry *d_entry;
|
|
|
+
|
|
|
+ d_entry = &(d_table->entries[dlg->h_entry]);
|
|
|
+
|
|
|
+ dlg_lock(d_table, d_entry);
|
|
|
+
|
|
|
+ dlg->h_id = d_entry->next_id++;
|
|
|
+ if (d_entry->first == 0) {
|
|
|
+ d_entry->first = d_entry->last = dlg;
|
|
|
+ } else {
|
|
|
+ d_entry->last->next = dlg;
|
|
|
+ dlg->prev = d_entry->last;
|
|
|
+ d_entry->last = dlg;
|
|
|
+ }
|
|
|
+
|
|
|
+ ref_dlg_unsafe(dlg, 1 + n);
|
|
|
+
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Refefence a dialog with locking
|
|
|
+ * \see ref_dlg_unsafe
|
|
|
+ * \param dlg dialog
|
|
|
+ * \param cnt increment for the reference counter
|
|
|
+ */
|
|
|
+void ref_dlg(struct dlg_cell *dlg, unsigned int cnt) {
|
|
|
+
|
|
|
+ struct dlg_entry *d_entry;
|
|
|
+
|
|
|
+ d_entry = &(d_table->entries[dlg->h_entry]);
|
|
|
+
|
|
|
+ dlg_lock(d_table, d_entry);
|
|
|
+ ref_dlg_unsafe(dlg, cnt);
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Unreference a dialog with locking
|
|
|
+ * \see unref_dlg_unsafe
|
|
|
+ * \param dlg dialog
|
|
|
+ * \param cnt decrement for the reference counter
|
|
|
+ */
|
|
|
+void unref_dlg(struct dlg_cell *dlg, unsigned int cnt) {
|
|
|
+
|
|
|
+ struct dlg_entry *d_entry;
|
|
|
+
|
|
|
+ d_entry = &(d_table->entries[dlg->h_entry]);
|
|
|
+
|
|
|
+ dlg_lock(d_table, d_entry);
|
|
|
+ unref_dlg_unsafe(dlg, cnt, d_entry);
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * Small logging helper functions for next_state_dlg.
|
|
|
+ * \param event logged event
|
|
|
+ * \param dlg dialog data
|
|
|
+ * \see next_state_dlg
|
|
|
+ */
|
|
|
+static inline void log_next_state_dlg(const int event, const struct dlg_cell * dlg) {
|
|
|
+
|
|
|
+ LM_CRIT("bogus event %d in state %d for dlg %p [%u:%u] with clid '%.*s' and tags "
|
|
|
+ "'%.*s'\n", event, dlg->state, dlg, dlg->h_entry, dlg->h_id,
|
|
|
+ dlg->callid.len, dlg->callid.s,
|
|
|
+ dlg->from_tag.len, dlg->from_tag.s);
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Update a dialog state according a event and the old state
|
|
|
+ *
|
|
|
+ * This functions implement the main state machine that update a dialog
|
|
|
+ * state according a processed event and the current state. If necessary
|
|
|
+ * it will delete the processed dialog. The old and new state are also
|
|
|
+ * saved for reference.
|
|
|
+ * \param dlg updated dialog
|
|
|
+ * \param event current event
|
|
|
+ * \param old_state old dialog state
|
|
|
+ * \param new_state new dialog state
|
|
|
+ * \param unref set to 1 when the dialog was deleted, 0 otherwise
|
|
|
+ */
|
|
|
+void next_state_dlg(struct dlg_cell *dlg, int event,
|
|
|
+ int *old_state, int *new_state, int *unref, str * to_tag) {
|
|
|
+ struct dlg_entry *d_entry;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ d_entry = &(d_table->entries[dlg->h_entry]);
|
|
|
+
|
|
|
+ *unref = 0;
|
|
|
+
|
|
|
+ dlg_lock(d_table, d_entry);
|
|
|
+
|
|
|
+ *old_state = dlg->state;
|
|
|
+
|
|
|
+ struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
|
|
|
+ struct dlg_cell_out *dlg_out;
|
|
|
+ dlg_out = d_entry_out->first;
|
|
|
+ int found = -1;
|
|
|
+
|
|
|
+ switch (event) {
|
|
|
+ case DLG_EVENT_TDEL:
|
|
|
+ switch (dlg->state) {
|
|
|
+ case DLG_STATE_UNCONFIRMED:
|
|
|
+ case DLG_STATE_EARLY:
|
|
|
+ dlg->state = DLG_STATE_DELETED;
|
|
|
+ unref_dlg_unsafe(dlg, 1, d_entry);
|
|
|
+ *unref = 1;
|
|
|
+ break;
|
|
|
+ case DLG_STATE_CONFIRMED:
|
|
|
+ unref_dlg_unsafe(dlg, 1, d_entry);
|
|
|
+ break;
|
|
|
+ case DLG_STATE_DELETED:
|
|
|
+ *unref = 1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_next_state_dlg(event, dlg);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DLG_EVENT_RPL1xx:
|
|
|
+ switch (dlg->state) {
|
|
|
+ case DLG_STATE_UNCONFIRMED:
|
|
|
+ case DLG_STATE_EARLY:
|
|
|
+ dlg->state = DLG_STATE_EARLY;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_next_state_dlg(event, dlg);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DLG_EVENT_RPL3xx:
|
|
|
+ switch (dlg->state) {
|
|
|
+ case DLG_STATE_UNCONFIRMED:
|
|
|
+ case DLG_STATE_EARLY:
|
|
|
+ dlg->state = DLG_STATE_DELETED;
|
|
|
+ *unref = 1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_next_state_dlg(event, dlg);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DLG_EVENT_RPL2xx:
|
|
|
+ switch (dlg->state) {
|
|
|
+ case DLG_STATE_DELETED:
|
|
|
+ if (dlg->dflags & DLG_FLAG_HASBYE) {
|
|
|
+ LM_CRIT("bogus event %d in state %d (with BYE) "
|
|
|
+ "for dlg %p [%u:%u] with clid '%.*s' and tags '%.*s' \n",
|
|
|
+ event, dlg->state, dlg, dlg->h_entry, dlg->h_id,
|
|
|
+ dlg->callid.len, dlg->callid.s,
|
|
|
+ dlg->from_tag.len, dlg->from_tag.s);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ref_dlg_unsafe(dlg, 1);
|
|
|
+ case DLG_STATE_UNCONFIRMED:
|
|
|
+ case DLG_STATE_EARLY:
|
|
|
+ dlg->state = DLG_STATE_CONFIRMED;
|
|
|
+ //TODO: check that the callbacks for confirmed are run
|
|
|
+ break;
|
|
|
+ case DLG_STATE_CONFIRMED:
|
|
|
+ //check the to_tag passed parameter exists
|
|
|
+ if (to_tag) {
|
|
|
+ //compare the to_tag passed parameter to the dlg_out to_tag entry of the dlg parameter (There should be only 1 dlg_out entry)
|
|
|
+
|
|
|
+ if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) {
|
|
|
+ //this parameter matches the existing dlg_out and is therefore a retransmission, so break
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ LM_ERR("It looks like this is a concurrently confirmed call!!\n");
|
|
|
+ LM_ERR("Error checking now so not putting into DLG_STATE_CONCURRENTLY_CONFIRMED\n");
|
|
|
+
|
|
|
+ LM_ERR("This is event DLG_EVENT_RPL2XX and the current dlg state is DLG_STATE_CONFIRMED\n");
|
|
|
+ LM_ERR("There should only be one dlg out here as the state is CONFIRMED but we are checking anyway!\n");
|
|
|
+ LM_ERR("To tag passed in the 2XX: [%.*s]", to_tag->len, to_tag->s);
|
|
|
+ LM_ERR("Now printing dlgouts totags - there should be only one!\n");
|
|
|
+ while (dlg_out) {
|
|
|
+ LM_ERR("dlg_out to_tag: [%.*s]\n", dlg_out->to_tag.len, dlg_out->to_tag.s);
|
|
|
+ dlg_out = dlg_out->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ //The parameter does not match so this is a concurrently confirmed call
|
|
|
+ //dlg->state = DLG_STATE_CONCURRENTLY_CONFIRMED;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ //to_tag parameter does not exist so break
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case DLG_STATE_CONCURRENTLY_CONFIRMED:
|
|
|
+
|
|
|
+ //check the to_tag passed parameter exists
|
|
|
+
|
|
|
+ if (to_tag) {
|
|
|
+
|
|
|
+ //compare the to_tag passed parameter to all the dlg_out to_tag entry of the dlg parameter (There could be multiple)
|
|
|
+ while (dlg_out) {
|
|
|
+
|
|
|
+ if (dlg_out->to_tag.len == to_tag->len && memcmp(dlg_out->to_tag.s, to_tag->s, dlg_out->to_tag.len) == 0) {
|
|
|
+ //this parameter matches the existing dlg_out and is therefore a retransmission
|
|
|
+ found = 1;
|
|
|
+ }
|
|
|
+ dlg_out = dlg_out->next;
|
|
|
+ }
|
|
|
+ if (found == -1) {
|
|
|
+ //The parameter does not match so this is another concurrently confirmed call (we would have breaked by now if it matched)
|
|
|
+ dlg->state = DLG_STATE_CONCURRENTLY_CONFIRMED;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ //to_tag parameter does not exist so break
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_next_state_dlg(event, dlg);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DLG_EVENT_REQACK:
|
|
|
+ switch (dlg->state) {
|
|
|
+ case DLG_STATE_CONFIRMED:
|
|
|
+ break;
|
|
|
+ case DLG_STATE_DELETED:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_next_state_dlg(event, dlg);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DLG_EVENT_REQBYE:
|
|
|
+ switch (dlg->state) {
|
|
|
+ case DLG_STATE_CONFIRMED:
|
|
|
+ dlg->dflags |= DLG_FLAG_HASBYE;
|
|
|
+ dlg->state = DLG_STATE_DELETED;
|
|
|
+ *unref = 1;
|
|
|
+ break;
|
|
|
+ case DLG_STATE_EARLY:
|
|
|
+ case DLG_STATE_DELETED:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_next_state_dlg(event, dlg);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DLG_EVENT_REQPRACK:
|
|
|
+ switch (dlg->state) {
|
|
|
+ case DLG_STATE_EARLY:
|
|
|
+ //Richard added this - think it is necessary as can received PRACK in early state!
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_next_state_dlg(event, dlg);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DLG_EVENT_REQ:
|
|
|
+ switch (dlg->state) {
|
|
|
+
|
|
|
+ case DLG_STATE_EARLY:
|
|
|
+ case DLG_STATE_CONFIRMED:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_next_state_dlg(event, dlg);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LM_CRIT("unknown event %d in state %d "
|
|
|
+ "for dlg %p [%u:%u] with clid '%.*s' and tags '%.*s'\n",
|
|
|
+ event, dlg->state, dlg, dlg->h_entry, dlg->h_id,
|
|
|
+ dlg->callid.len, dlg->callid.s,
|
|
|
+ dlg->from_tag.len, dlg->from_tag.s);
|
|
|
+ }
|
|
|
+ *new_state = dlg->state;
|
|
|
+
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+
|
|
|
+ LM_DBG("dialog %p changed from state %d to "
|
|
|
+ "state %d, due event %d\n", dlg, *old_state, *new_state, event);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ */
|
|
|
+int dlg_set_toroute(struct dlg_cell *dlg, str * route) {
|
|
|
+ if (dlg == NULL || route == NULL || route->len <= 0)
|
|
|
+ return 0;
|
|
|
+ if (dlg->toroute_name.s != NULL) {
|
|
|
+ shm_free(dlg->toroute_name.s);
|
|
|
+ dlg->toroute_name.s = NULL;
|
|
|
+ dlg->toroute_name.len = 0;
|
|
|
+ }
|
|
|
+ dlg->toroute_name.s = (char*) shm_malloc((route->len + 1) * sizeof (char));
|
|
|
+ if (dlg->toroute_name.s == NULL) {
|
|
|
+ LM_ERR("no more shared memory\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ memcpy(dlg->toroute_name.s, route->s, route->len);
|
|
|
+ dlg->toroute_name.len = route->len;
|
|
|
+ dlg->toroute_name.s[dlg->toroute_name.len] = '\0';
|
|
|
+ dlg->toroute = route_lookup(&main_rt, dlg->toroute_name.s);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Takes the did of the dialog and appends an "x" to it to make a different did for concurrent calls
|
|
|
+ * \param dlg_cell - dlg_cell whose did we use
|
|
|
+ * \param new_did - empty container for new_did
|
|
|
+ * \return void
|
|
|
+ */
|
|
|
+void create_concurrent_did(struct dlg_cell *dlg, str * new_did) {
|
|
|
+ int len = dlg->did.len + 1;
|
|
|
+ new_did = shm_malloc(len);
|
|
|
+ if (new_did == 0) {
|
|
|
+ LM_ERR("no more shm mem (%d)\n", len);
|
|
|
+ }
|
|
|
+ memcpy(new_did->s, dlg->did.s, dlg->did.len);
|
|
|
+ new_did->s[dlg->did.len+1]= 'x';
|
|
|
+ new_did->len = len;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Update the did of the dlg_out structure
|
|
|
+ * \param dlg_cell_out - structure to update
|
|
|
+ * \param new_did - new did to use
|
|
|
+ * \return 1 success, 0 failure
|
|
|
+ */
|
|
|
+int update_dlg_out_did(struct dlg_cell_out *dlg_out, str * new_did) {
|
|
|
+ //update the did of the dlg_out
|
|
|
+ if (dlg_out->did.s) {
|
|
|
+ if (dlg_out->did.len < new_did->len) {
|
|
|
+ shm_free(dlg_out->did.s);
|
|
|
+ dlg_out->did.s = (char*) shm_malloc(new_did->len);
|
|
|
+ if (dlg_out->did.s == NULL)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dlg_out->did.s = (char*) shm_malloc(new_did->len);
|
|
|
+ if (dlg_out->did.s == NULL)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ memcpy(dlg_out->did.s, new_did->s, new_did->len);
|
|
|
+ dlg_out->did.len = new_did->len;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+error:
|
|
|
+ LM_ERR("not more shm mem\n");
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Update the did of the dlg structure
|
|
|
+ * \param dlg_cell - structure to update
|
|
|
+ * \param new_did - new did to use
|
|
|
+ * \return 1 success, 0 failure
|
|
|
+ */
|
|
|
+int update_dlg_did(struct dlg_cell *dlg, str * new_did) {
|
|
|
+ //update the did of the dlg_out
|
|
|
+ if (dlg->did.s) {
|
|
|
+ if (dlg->did.len < new_did->len) {
|
|
|
+ shm_free(dlg->did.s);
|
|
|
+ dlg->did.s = (char*) shm_malloc(new_did->len);
|
|
|
+ if (dlg->did.s == NULL)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dlg->did.s = (char*) shm_malloc(new_did->len);
|
|
|
+ if (dlg->did.s == NULL)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ memcpy(dlg->did.s, new_did->s, new_did->len);
|
|
|
+ dlg->did.len = new_did->len;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+error:
|
|
|
+ LM_ERR("not more shm mem\n");
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/**************************** MI functions ******************************/
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Helper method that output a dialog via the MI interface
|
|
|
+ * \see mi_print_dlg
|
|
|
+ * \param rpl MI node that should be filled
|
|
|
+ * \param dlg printed dialog
|
|
|
+ * \param with_context if 1 then the dialog context will be also printed
|
|
|
+ * \return 0 on success, -1 on failure
|
|
|
+ */
|
|
|
+static inline int internal_mi_print_dlg_out(struct mi_node *rpl,
|
|
|
+ struct dlg_cell_out * dlg_out) {
|
|
|
+ struct mi_node* node = NULL;
|
|
|
+ struct mi_node* node1 = NULL;
|
|
|
+ struct mi_attr* attr = NULL;
|
|
|
+
|
|
|
+ node = add_mi_node_child(rpl, 0, "dialog_out", 10, 0, 0);
|
|
|
+ if (node == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ attr = addf_mi_attr(node, 0, "hash", 4, "%u:%u",
|
|
|
+ dlg_out->h_entry, dlg_out->h_id);
|
|
|
+ if (attr == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "to_tag", 6,
|
|
|
+ dlg_out->to_tag.s, dlg_out->to_tag.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "did", 3,
|
|
|
+ dlg_out->did.s, dlg_out->did.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_contact", 14,
|
|
|
+ dlg_out->callee_contact.s,
|
|
|
+ dlg_out->callee_contact.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_cseq", 11,
|
|
|
+ dlg_out->caller_cseq.s,
|
|
|
+ dlg_out->caller_cseq.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_cseq", 11,
|
|
|
+ dlg_out->callee_cseq.s,
|
|
|
+ dlg_out->callee_cseq.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "callee_route_set", 16,
|
|
|
+ dlg_out->callee_route_set.s,
|
|
|
+ dlg_out->callee_route_set.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ if (dlg_out->callee_bind_addr) {
|
|
|
+ node1 = add_mi_node_child(node, 0,
|
|
|
+ "callee_bind_addr", 16,
|
|
|
+ dlg_out->callee_bind_addr->sock_str.s,
|
|
|
+ dlg_out->callee_bind_addr->sock_str.len);
|
|
|
+ } else {
|
|
|
+ node1 = add_mi_node_child(node, 0,
|
|
|
+ "callee_bind_addr", 16, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ LM_ERR("failed to add node\n");
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Helper method that output a dialog via the MI interface
|
|
|
+ * \see mi_print_dlg
|
|
|
+ * \param rpl MI node that should be filled
|
|
|
+ * \param dlg printed dialog
|
|
|
+ * \param with_context if 1 then the dialog context will be also printed
|
|
|
+ * \return 0 on success, -1 on failure
|
|
|
+ */
|
|
|
+static inline int internal_mi_print_dlg(struct mi_node *rpl,
|
|
|
+ struct dlg_cell *dlg, int with_context) {
|
|
|
+ struct mi_node* node = NULL;
|
|
|
+ struct mi_node* node1 = NULL;
|
|
|
+ struct mi_attr* attr = NULL;
|
|
|
+ int len;
|
|
|
+ char* p;
|
|
|
+
|
|
|
+ node = add_mi_node_child(rpl, 0, "dialog", 6, 0, 0);
|
|
|
+ if (node == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ attr = addf_mi_attr(node, 0, "hash", 4, "%u:%u",
|
|
|
+ dlg->h_entry, dlg->h_id);
|
|
|
+ if (attr == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ p = int2str((unsigned long) dlg->state, &len);
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "state", 5, p, len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ p = int2str((unsigned long) dlg->ref, &len);
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "ref_count", 9, p, len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ p = int2str((unsigned long) dlg->start_ts, &len);
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "timestart", 9, p, len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ p = int2str((unsigned long) dlg->tl.timeout, &len);
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "timeout", 7, p, len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "callid", 6,
|
|
|
+ dlg->callid.s, dlg->callid.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "from_uri", 8,
|
|
|
+ dlg->from_uri.s, dlg->from_uri.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "from_tag", 8,
|
|
|
+ dlg->from_tag.s, dlg->from_tag.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "did", 3,
|
|
|
+ dlg->did.s, dlg->did.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_contact", 14,
|
|
|
+ dlg->caller_contact.s,
|
|
|
+ dlg->caller_contact.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "first_req_cseq", 14,
|
|
|
+ dlg->first_req_cseq.s,
|
|
|
+ dlg->first_req_cseq.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ node1 = add_mi_node_child(node, MI_DUP_VALUE, "caller_route_set", 16,
|
|
|
+ dlg->caller_route_set.s,
|
|
|
+ dlg->caller_route_set.len);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ if (dlg->caller_bind_addr) {
|
|
|
+ node1 = add_mi_node_child(node, 0,
|
|
|
+ "caller_bind_addr", 16,
|
|
|
+ dlg->caller_bind_addr->sock_str.s,
|
|
|
+ dlg->caller_bind_addr->sock_str.len);
|
|
|
+ } else {
|
|
|
+ node1 = add_mi_node_child(node, 0,
|
|
|
+ "caller_bind_addr", 16, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (with_context) {
|
|
|
+ node1 = add_mi_node_child(node, 0, "context", 7, 0, 0);
|
|
|
+ if (node1 == 0)
|
|
|
+ goto error;
|
|
|
+ run_dlg_callbacks(DLGCB_MI_CONTEXT,
|
|
|
+ dlg,
|
|
|
+ NULL,
|
|
|
+ NULL,
|
|
|
+ DLG_DIR_NONE,
|
|
|
+ (void *) node1);
|
|
|
+ }
|
|
|
+
|
|
|
+ struct dlg_cell_out *dlg_out;
|
|
|
+ struct dlg_entry_out *d_entry_out = &(dlg->dlg_entry_out);
|
|
|
+ dlg_out = d_entry_out->first;
|
|
|
+
|
|
|
+ while (dlg_out) {
|
|
|
+
|
|
|
+ if (internal_mi_print_dlg_out(rpl, dlg_out) != 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ dlg_out = dlg_out->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ LM_ERR("failed to add node\n");
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Output a dialog via the MI interface
|
|
|
+ * \param rpl MI node that should be filled
|
|
|
+ * \param dlg printed dialog
|
|
|
+ * \param with_context if 1 then the dialog context will be also printed
|
|
|
+ * \return 0 on success, -1 on failure
|
|
|
+ */
|
|
|
+int mi_print_dlg(struct mi_node *rpl, struct dlg_cell *dlg, int with_context) {
|
|
|
+
|
|
|
+ return internal_mi_print_dlg(rpl, dlg, with_context);
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Helper function that output all dialogs via the MI interface
|
|
|
+ * \see mi_print_dlgs
|
|
|
+ * \param rpl MI node that should be filled
|
|
|
+ * \param with_context if 1 then the dialog context will be also printed
|
|
|
+ * \return 0 on success, -1 on failure
|
|
|
+ */
|
|
|
+static int internal_mi_print_dlgs(struct mi_node *rpl, int with_context) {
|
|
|
+ struct dlg_cell *dlg;
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ LM_DBG("printing %i dialogs\n", d_table->size);
|
|
|
+
|
|
|
+ for (i = 0; i < d_table->size; i++) {
|
|
|
+ dlg_lock(d_table, &(d_table->entries[i]));
|
|
|
+
|
|
|
+ for (dlg = d_table->entries[i].first; dlg; dlg = dlg->next) {
|
|
|
+ if (internal_mi_print_dlg(rpl, dlg, with_context) != 0)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ dlg_unlock(d_table, &(d_table->entries[i]));
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+
|
|
|
+error:
|
|
|
+ dlg_unlock(d_table, &(d_table->entries[i]));
|
|
|
+ LM_ERR("failed to print dialog\n");
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+static inline struct mi_root * process_mi_params(struct mi_root *cmd_tree, struct dlg_cell **dlg_p) {
|
|
|
+ struct mi_node* node;
|
|
|
+ struct dlg_entry *d_entry;
|
|
|
+ struct dlg_cell *dlg;
|
|
|
+ str *callid;
|
|
|
+ str *from_tag;
|
|
|
+ unsigned int h_entry;
|
|
|
+
|
|
|
+ node = cmd_tree->node.kids;
|
|
|
+ if (node == NULL) {
|
|
|
+ /* no parameters at all */
|
|
|
+ *dlg_p = NULL;
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* we have params -> get callid and fromtag */
|
|
|
+ callid = &node->value;
|
|
|
+ LM_DBG("callid='%.*s'\n", callid->len, callid->s);
|
|
|
+
|
|
|
+ node = node->next;
|
|
|
+ if (!node || !node->value.s || !node->value.len) {
|
|
|
+ from_tag = NULL;
|
|
|
+ } else {
|
|
|
+ from_tag = &node->value;
|
|
|
+ LM_DBG("from_tag='%.*s'\n", from_tag->len, from_tag->s);
|
|
|
+ if (node->next != NULL)
|
|
|
+ return init_mi_tree(400, MI_SSTR(MI_MISSING_PARM));
|
|
|
+ }
|
|
|
+
|
|
|
+ h_entry = core_hash(callid, 0, d_table->size);
|
|
|
+
|
|
|
+ d_entry = &(d_table->entries[h_entry]);
|
|
|
+ dlg_lock(d_table, d_entry);
|
|
|
+
|
|
|
+ for (dlg = d_entry->first; dlg; dlg = dlg->next) {
|
|
|
+ if (match_downstream_dialog(dlg, callid, from_tag) == 1) {
|
|
|
+ if (dlg->state == DLG_STATE_DELETED) {
|
|
|
+ *dlg_p = NULL;
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ *dlg_p = dlg;
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dlg_unlock(d_table, d_entry);
|
|
|
+
|
|
|
+ return init_mi_tree(404, MI_SSTR("Nu such dialog"));
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Output all dialogs via the MI interface
|
|
|
+ * \param cmd_tree MI command tree
|
|
|
+ * \param param unused
|
|
|
+ * \return mi node with the dialog information, or NULL on failure
|
|
|
+ */
|
|
|
+
|
|
|
+struct mi_root * mi_print_dlgs(struct mi_root *cmd_tree, void *param) {
|
|
|
+ struct mi_root* rpl_tree = NULL;
|
|
|
+ struct mi_node* rpl = NULL;
|
|
|
+ struct dlg_cell* dlg = NULL;
|
|
|
+
|
|
|
+ rpl_tree = process_mi_params(cmd_tree, &dlg);
|
|
|
+ if (rpl_tree)
|
|
|
+ //param error
|
|
|
+ return rpl_tree;
|
|
|
+
|
|
|
+ rpl_tree = init_mi_tree(200, MI_SSTR(MI_OK));
|
|
|
+ if (rpl_tree == 0)
|
|
|
+ return 0;
|
|
|
+ rpl = &rpl_tree->node;
|
|
|
+
|
|
|
+ if (dlg == NULL) {
|
|
|
+ if (internal_mi_print_dlgs(rpl, 0) != 0)
|
|
|
+ goto error;
|
|
|
+ } else {
|
|
|
+ if (internal_mi_print_dlg(rpl, dlg, 0) != 0)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rpl_tree;
|
|
|
+error:
|
|
|
+ free_mi_tree(rpl_tree);
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/*!
|
|
|
+ * \brief Print a dialog context via the MI interface
|
|
|
+ * \param cmd_tree MI command tree
|
|
|
+ * \param param unused
|
|
|
+ * \return mi node with the dialog information, or NULL on failure
|
|
|
+ */
|
|
|
+struct mi_root * mi_print_dlgs_ctx(struct mi_root *cmd_tree, void *param) {
|
|
|
+ struct mi_root* rpl_tree = NULL;
|
|
|
+ struct mi_node* rpl = NULL;
|
|
|
+ struct dlg_cell* dlg = NULL;
|
|
|
+
|
|
|
+ rpl_tree = process_mi_params(cmd_tree, &dlg);
|
|
|
+ if (rpl_tree)
|
|
|
+ /* param error */
|
|
|
+ return rpl_tree;
|
|
|
+
|
|
|
+ rpl_tree = init_mi_tree(200, MI_SSTR(MI_OK));
|
|
|
+ if (rpl_tree == 0)
|
|
|
+ return 0;
|
|
|
+ rpl = &rpl_tree->node;
|
|
|
+
|
|
|
+ if (dlg == NULL) {
|
|
|
+ if (internal_mi_print_dlgs(rpl, 1) != 0)
|
|
|
+ goto error;
|
|
|
+ } else {
|
|
|
+ if (internal_mi_print_dlg(rpl, dlg, 1) != 0)
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rpl_tree;
|
|
|
+error:
|
|
|
+ free_mi_tree(rpl_tree);
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+time_t api_get_dlg_expires(str *callid, str *ftag, str *ttag) {
|
|
|
+ struct dlg_cell *dlg;
|
|
|
+ time_t expires = 0;
|
|
|
+ time_t start;
|
|
|
+
|
|
|
+ if (!callid || !ftag || !ttag) {
|
|
|
+ LM_ERR("Missing callid, from tag or to tag\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ unsigned int direction = DLG_DIR_NONE;
|
|
|
+ dlg = get_dlg(callid, ftag, ttag, &direction);
|
|
|
+ if (!dlg) return 0;
|
|
|
+
|
|
|
+ if (dlg->state != DLG_STATE_CONFIRMED || !dlg->start_ts) {
|
|
|
+ /* Dialog not started yet so lets assume start time is now.*/
|
|
|
+ start = time(0);
|
|
|
+ } else {
|
|
|
+ start = dlg->start_ts;
|
|
|
+ }
|
|
|
+
|
|
|
+ expires = start + dlg->lifetime;
|
|
|
+ unref_dlg(dlg, 1);
|
|
|
+
|
|
|
+ return expires;
|
|
|
+}
|
|
|
+
|
|
|
+char* state_to_char(unsigned int state) {
|
|
|
+ switch (state) {
|
|
|
+ case DLG_STATE_UNCONFIRMED:
|
|
|
+ return "Unconfirmed";
|
|
|
+ case DLG_STATE_EARLY:
|
|
|
+ return "Early";
|
|
|
+ case DLG_STATE_CONFIRMED:
|
|
|
+ return "Confirmed";
|
|
|
+ case DLG_STATE_DELETED:
|
|
|
+ return "Deleted";
|
|
|
+ default:
|
|
|
+ return "Unknown";
|
|
|
+ }
|
|
|
+}
|