Esempio n. 1
0
/*!
 * \brief Function that is registered as TM callback and called on replies
 *
 * Function that is registered as TM callback and called on replies. It
 * parses the reply and set the appropriate event. This is then used to
 * update the dialog state, run eventual dialog callbacks and save or
 * update the necessary informations about the dialog.
 * \see next_state_dlg
 * \param t transaction, unused
 * \param type type of the entered callback
 * \param param saved dialog structure in the callback
 */
static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param) {
    struct dlg_cell *dlg;
    struct dlg_cell_out *dlg_out = 0;

    int new_state, old_state, unref, event;
    str to_tag, to_uri;
    struct sip_msg *req = param->req;
    struct sip_msg *rpl = param->rpl;
    struct dlg_entry_out* dlg_entry_out = 0;

    if (t && t->fwded_totags)
        LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i] and TO is [%.*s]\n", type, t->fwded_totags->tag.len, t->fwded_totags->tag.s);
    else
        LM_DBG("ONREPLY CALL_BACK from TM received and type is [%i]\n", type);

    dlg = (struct dlg_cell *) (*param->param);
    if (shutdown_done || dlg == 0)
        return;

    if (t) {
        dlg->transaction = t;
    }

    LM_DBG("DLG dialogid is entry:id [%i:%i]\n", dlg->h_entry, dlg->h_id);

    if (type == TMCB_RESPONSE_FWDED) {
        // The state does not change, but the msg is mutable in this callback
        LM_DBG("TMCB_RESPONSE_FWDED from TM received");
        run_dlg_callbacks(DLGCB_RESPONSE_FWDED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
        return;
    }

    if (type == TMCB_RESPONSE_OUT) {
        LM_DBG("TMCB_RESPONSE_OUT\n");
        return;
    }

    if (type == TMCB_RESPONSE_READY) {
        if (rpl == FAKED_REPLY) {
            LM_DBG("Faked reply\n");
            return;
        }

        // get to tag
        LM_DBG("Extracting to-tag from reply");
        if (!rpl->to && ((parse_headers(rpl, HDR_TO_F, 0) < 0) || !rpl->to)) {
            LM_ERR("bad reply or missing TO hdr :-/\n");
            to_tag.s = 0;
            to_tag.len = 0;
        } else {
            //populate to uri for this branch.
            to_uri = get_to(rpl)->uri;

            to_tag = get_to(rpl)->tag_value;
            if (to_tag.s == 0 || to_tag.len == 0) {
                LM_ERR("missing TAG param in TO hdr :-/\n");
                to_tag.s = 0;
                to_tag.len = 0;
                //Here we assume that the transaction module timer will remove any early dialogs
                return;
            }
        }

        LM_DBG("Got to-tag from response: %.*s \n", to_tag.len, to_tag.s);
    }

    if (type == TMCB_DESTROY)
        event = DLG_EVENT_TDEL;
    else if (param->code < 200)
        event = DLG_EVENT_RPL1xx;
    else if (param->code < 300)
        event = DLG_EVENT_RPL2xx;
    else
        event = DLG_EVENT_RPL3xx;

    LM_DBG("Calling next_state_dlg and event is %i\n", event);
    next_state_dlg(dlg, event, &old_state, &new_state, &unref, &to_tag);

    if (type == TMCB_RESPONSE_READY) {
        LM_DBG("Checking if there is an existing dialog_out entry with same to-tag");

        dlg_entry_out = &dlg->dlg_entry_out;

        lock_get(dlg->dlg_out_entries_lock);
        dlg_out = dlg_entry_out->first;

        LM_DBG("Scanning dlg_entry_out list for dlg_out");
        while (dlg_out) {
            //Check if there is an already dialog_out entry with same To-tag
            if (dlg_out->to_tag.len == to_tag.len &&
                    memcmp(dlg_out->to_tag.s, to_tag.s, dlg_out->to_tag.len) == 0) {
                //Found a dialog_out entry with same to_tag!
                LM_DBG("Found dlg_out for to-tag: %.*s\n", dlg_out->to_tag.len, dlg_out->to_tag.s);
                break;
            }
            dlg_out = dlg_out->next;
        }
        lock_release(dlg->dlg_out_entries_lock);

        if (!dlg_out) {
            LM_DBG("No dlg_out entry found - creating a new dialog_out entry on dialog [%p]\n", dlg);
            dlg_out = build_new_dlg_out(dlg, &to_uri, &to_tag);

            link_dlg_out(dlg, dlg_out, 0);

            /* save callee's cseq, caller cseq, callee contact and callee record route*/
            if (populate_leg_info(dlg, rpl, t, DLG_CALLEE_LEG, &to_tag) != 0) {
                LM_ERR("could not add further info to the dlg out\n");
            }

            if (!dlg_out) {
                LM_ERR("failed to create new dialog out structure\n");
                //TODO do something on this error!

            }
        } else {
            //This dlg_out already exists, update cseq and contact if present

            LM_DBG("dlg_out entry found - updating cseq's for dialog out [%p] for to-tag [%.*s] \n", dlg_out, dlg_out->to_tag.len, dlg_out->to_tag.s);

            if ((!rpl->cseq && parse_headers(rpl, HDR_CSEQ_F, 0) < 0) || !rpl->cseq ||
                    !rpl->cseq->parsed) {
                LM_ERR("bad sip message or missing CSeq hdr :-/\n");
            }
            dlg_update_cseq(dlg, DLG_CALLEE_LEG, &((get_cseq(rpl))->number), &(dlg_out->to_tag));


            /* extract the contact address to update if present*/
            if (!rpl->contact && (parse_headers(rpl, HDR_CONTACT_F, 0) < 0 || !rpl->contact)) {
                LM_ERR("Can not update callee contact: bad sip message or missing Contact hdr\n");
            }
            else if (parse_contact(rpl->contact) < 0 ||
                    ((contact_body_t *) rpl->contact->parsed)->contacts == NULL ||
                    ((contact_body_t *) rpl->contact->parsed)->contacts->next != NULL) {
                LM_ERR("Can not update callee contact: bad Contact HDR\n");
            }
            else
            {
                str contact;
                contact = ((contact_body_t *) rpl->contact->parsed)->contacts->uri;
                dlg_update_contact(dlg, DLG_CALLEE_LEG, &contact, &(dlg_out->to_tag));
            }

        }
    }

    if (new_state == DLG_STATE_EARLY) {
        run_dlg_callbacks(DLGCB_EARLY, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
        return;
    }

    LM_DBG("new state is %i and old state is %i\n", new_state, old_state);

    if ((new_state == DLG_STATE_CONFIRMED) && (event == DLG_EVENT_RPL2xx)) {
        LM_DBG("dialog %p confirmed \n", dlg);
        //Remove all the other entries in dialog_out for the same dialog after TM expires the transaction
        //(not before in order to absorb late in-early-dialog requests).

        //remove all other dlg_out objects
        if (dlg_out) {
            if (d_tmb.register_tmcb(req, NULL, TMCB_DESTROY, unlink_dlgouts_from_cb, (void*) dlg, NULL) < 0) {
                LM_ERR("failed to register deletion delay function\n");
                LM_DBG("Removing all other DLGs");
                dlg_remove_dlg_out(dlg_out, dlg, 0);
            } else {
                //mark the outs for deletion
                dlg_remove_dlg_out(dlg_out, dlg, 1);

            }
        } else {
            LM_ERR("There is no dlg_out structure - this is bad\n");
            //TODO: add error handling here
        }

        /* set start time */
        dlg->start_ts = (unsigned int) (time(0));

        /* save the settings to the database,
         * if realtime saving mode configured- save dialog now
         * else: the next time the timer will fire the update*/
        dlg->dflags |= DLG_FLAG_NEW;

        if (0 != insert_dlg_timer(&dlg->tl, dlg->lifetime)) {
            LM_CRIT("Unable to insert dlg %p [%u:%u] on event %d [%d->%d] "
                    "with clid '%.*s' and tags '%.*s' \n",
                    dlg, dlg->h_entry, dlg->h_id, event, old_state, new_state,
                    dlg->callid.len, dlg->callid.s,
                    dlg->from_tag.len, dlg->from_tag.s);

        } else {
            ref_dlg(dlg, 1);
        }

        run_dlg_callbacks(DLGCB_CONFIRMED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);

        if (unref) unref_dlg(dlg, unref);
        return;
    }

    if (new_state == DLG_STATE_CONCURRENTLY_CONFIRMED && (old_state == DLG_STATE_CONFIRMED || old_state == DLG_STATE_CONCURRENTLY_CONFIRMED)) {
        //This is a concurrently confirmed call
        LM_DBG("This is a concurrently confirmed call.");
        //Create a new Dialog ID token “X”
        //Not sure how to do this so just going to use existing Did and add an X character to it
        str new_did;
        create_concurrent_did(dlg, &new_did);

        //assign new did to the created or updated dialog_out entry.
        update_dlg_out_did(dlg_out, &new_did);

        //Then, duplicate the dialog_in entry and set its Dialog ID value to new_did
        //for now rather just create new dlg structure with the correct params - this should be fixed if future use requires

        struct dlg_cell *new_dlg = 0;
        new_dlg = build_new_dlg(&(dlg->callid) /*callid*/,
                &(dlg->from_uri) /*from uri*/,
                &(dlg->from_tag)/*from_tag*/,
                &(dlg->req_uri) /*r-uri*/);

        //assign new did to dlg_in
        update_dlg_did(new_dlg, &new_did);

        if (new_dlg == 0) {
            LM_ERR("failed to create new dialog\n");
            return;
        }

        //link the new_dlg with dlg_out object
        link_dlg_out(new_dlg, dlg_out, 0);

    }

    if (old_state != DLG_STATE_DELETED && new_state == DLG_STATE_DELETED) {
        LM_DBG("dialog %p failed (negative reply)\n", dlg);
        /* dialog setup not completed (3456XX) */
        run_dlg_callbacks(DLGCB_FAILED, dlg, req, rpl, DLG_DIR_UPSTREAM, 0);
        /* do unref */
        if (unref)
            unref_dlg(dlg, unref);

        return;
    }

    if (unref) unref_dlg(dlg, unref);

    return;
}
Esempio n. 2
0
/*!
 * \brief Create a new dialog from a sip message
 *
 * Create a new dialog from a SIP message, register a callback
 * to keep track of the dialog with help of the tm module.
 * This function is either called from the request callback, or
 * from the dlg_manage function in the configuration script.
 * \see dlg_onreq
 * \see w_dlg_manage
 * \param msg SIP message
 * \param t transaction
 * \return 0 on success, -1 on failure
 */
int dlg_new_dialog(struct sip_msg *req, struct cell *t, const int run_initial_cbs) {
    struct dlg_cell *dlg;
    str s;
    str callid;
    str ftag;
    str ttag;
    str req_uri;
    unsigned int dir;

    LM_DBG("starting dlg_new_dialog and method is [%.*s]\n", req->first_line.u.request.method.len, req->first_line.u.request.method.s);

    if (current_dlg_pointer != NULL)
        return -1;

    if (req->first_line.u.request.method_value == METHOD_CANCEL)
        return -1;

    if (pre_match_parse(req, &callid, &ftag, &ttag, 0) < 0) {
        LM_WARN("pre-matching failed\n");
        return -1;
    }

    if (ttag.s != 0 && ttag.len != 0)
        return -1;

    if (pv_printf_s(req, ruri_param_model, &req_uri) < 0) {
        LM_ERR("error - cannot print the r-uri format\n");
        return -1;
    }
    trim(&req_uri);

    if (detect_spirals) {
        if (spiral_detected == 1)
            return 0;

        dir = DLG_DIR_NONE;

        dlg = get_dlg(&callid, &ftag, &ttag, &dir);
        if (dlg) {
            LM_DBG("Callid '%.*s' found, must be a spiraled request\n",
                    callid.len, callid.s);
            spiral_detected = 1;

            if (run_initial_cbs)
                run_dlg_callbacks(DLGCB_SPIRALED, dlg, req, NULL, DLG_DIR_DOWNSTREAM, 0);

            //Add did to rr header for all spiralled requested INVITEs
            if (req->first_line.u.request.method_value == METHOD_INVITE) {
                if (add_dlg_rr_param(dlg, req, dlg->h_entry, dlg->h_id) < 0) {
                    LM_ERR("failed to add RR param\n");

                }
            }

            // get_dlg has incremented the ref count by 1
            unref_dlg(dlg, 1);
            goto finish;
        }
    }
    spiral_detected = 0;


    LM_DBG("Building new Dialog for call-id %.*s\n", callid.len, callid.s);
    LM_DBG("SIP Method: %.*s  \n", req->first_line.u.request.method.len, req->first_line.u.request.method.s);
    dlg = build_new_dlg(&callid /*callid*/,
            &(get_from(req)->uri) /*from uri*/,
            &ftag/*from_tag*/,
            &req_uri /*r-uri*/);

    if (dlg == 0) {
        LM_ERR("failed to create new dialog\n");
        return -1;
    }

    /* save caller's tag, cseq, contact and record route*/
    if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG,
            &(get_from(req)->tag_value)) != 0) {
        LM_ERR("could not add further info to the dialog\n");
        lock_destroy(dlg->dlg_out_entries_lock);
        lock_dealloc(dlg->dlg_out_entries_lock);
        shm_free(dlg);
        return -1;
    }

    dlg->transaction = t;

    /* Populate initial varlist: */
    dlg->vars = get_local_varlist_pointer(req, 1);

    link_dlg(dlg, 0);
    if (run_initial_cbs) run_create_callbacks(dlg, req);

    //Dialog will *always* use DID cookie so no need for match mode anymore
    if (add_dlg_rr_param(dlg, req, dlg->h_entry, dlg->h_id) < 0) {
        LM_ERR("failed to add RR param\n");
        goto error;
    }

    if (d_tmb.register_tmcb(req, t,
            TMCB_RESPONSE_READY | TMCB_RESPONSE_FWDED | TMCB_RESPONSE_OUT,
            dlg_onreply, (void*) dlg, unref_new_dialog) < 0) {
        LM_ERR("failed to register TMCB\n");
        goto error;
    }
    // increase reference counter because of registered callback
    ref_dlg(dlg, 1);

    dlg->lifetime = get_dlg_timeout(req);
    s.s = _dlg_ctx.to_route_name;
    s.len = strlen(s.s);
    dlg_set_toroute(dlg, &s);
    dlg->sflags |= _dlg_ctx.flags;

    if (_dlg_ctx.to_bye != 0)
        dlg->dflags |= DLG_FLAG_TOBYE;

finish:
    if (t) {
        // transaction exists ==> keep ref counter large enough to
        // avoid premature cleanup and ensure proper dialog referencing
        if (store_dlg_in_tm(req, t, dlg) < 0) {
            LM_ERR("failed to store dialog in transaction\n");
            goto error;
        }
    } else {
        // no transaction exists ==> postpone work until we see the
        // request being forwarded statefully
        if (d_tmb.register_tmcb(req, NULL, TMCB_REQUEST_FWDED,
                store_dlg_in_tm_cb, (void*) dlg, NULL) < 0) {
            LM_ERR("failed to register callback for storing dialog in transaction\n");
            goto error;
        }
    }

    LM_DBG("Setting current dialog\n");
    set_current_dialog(req, dlg);
    _dlg_ctx.dlg = dlg;
    ref_dlg(dlg, 1);
    return 0;

error:
    LM_DBG("Error in build_new_dlg");
    if (!spiral_detected)
        unref_dlg(dlg, 1); // undo ref regarding linking
    return -1;
}
Esempio n. 3
0
/**
* @brief ht dmq callback
*/
int dlg_dmq_handle_msg(struct sip_msg* msg, peer_reponse_t* resp, dmq_node_t* node)
{
	int content_length;
	str body;
	dlg_cell_t *dlg;
	int unref = 0;
	int ret;
	srjson_doc_t jdoc, prof_jdoc;
	srjson_t *it = NULL;

	dlg_dmq_action_t action = DLG_DMQ_NONE;
	dlg_iuid_t iuid;
	str profiles = {0, 0}, callid = {0, 0}, tag1 = {0,0}, tag2 = {0,0},
		contact1 = {0,0}, contact2 = {0,0}, k={0,0}, v={0,0};
	str cseq1 = {0,0}, cseq2 = {0,0}, route_set1 = {0,0}, route_set2 = {0,0},
		from_uri = {0,0}, to_uri = {0,0}, req_uri = {0,0};
	unsigned int init_ts = 0, start_ts = 0, lifetime = 0;
	unsigned int state = 1;
	srjson_t *vj;

	/* received dmq message */
	LM_DBG("dmq message received\n");

	if(!msg->content_length) {
		LM_ERR("no content length header found\n");
		goto invalid2;
	}
	content_length = get_content_length(msg);
	if(!content_length) {
		LM_DBG("content length is 0\n");
		goto invalid2;
	}

	body.s = get_body(msg);
	body.len = content_length;

	if (!body.s) {
		LM_ERR("unable to get body\n");
		goto error;
	}

	/* parse body */
	LM_DBG("body: %.*s\n", body.len, body.s);

	srjson_InitDoc(&jdoc, NULL);
	jdoc.buf = body;

	if(jdoc.root == NULL) {
		jdoc.root = srjson_Parse(&jdoc, jdoc.buf.s);
		if(jdoc.root == NULL)
		{
			LM_ERR("invalid json doc [[%s]]\n", jdoc.buf.s);
			goto invalid;
		}
	}

	for(it=jdoc.root->child; it; it = it->next)
	{
		if ((it->string == NULL) || (strcmp(it->string, "vars")==0)) continue;

		LM_DBG("found field: %s\n", it->string);

		if (strcmp(it->string, "action")==0) {
			action = SRJSON_GET_UINT(it);
		} else if (strcmp(it->string, "h_entry")==0) {
			iuid.h_entry = SRJSON_GET_UINT(it);
		} else if (strcmp(it->string, "h_id")==0) {
			iuid.h_id = SRJSON_GET_UINT(it);
		} else if (strcmp(it->string, "init_ts")==0) {
			init_ts = SRJSON_GET_UINT(it);
		} else if (strcmp(it->string, "start_ts")==0) {
			start_ts = SRJSON_GET_UINT(it);
		} else if (strcmp(it->string, "state")==0) {
			state = SRJSON_GET_UINT(it);
		} else if (strcmp(it->string, "lifetime")==0) {
			lifetime = SRJSON_GET_UINT(it);
		} else if (strcmp(it->string, "callid")==0) {
			callid.s = it->valuestring;
			callid.len = strlen(callid.s);
		} else if (strcmp(it->string, "profiles")==0) {
			profiles.s = it->valuestring;
			profiles.len = strlen(profiles.s);
		} else if (strcmp(it->string, "tag1")==0) {
			tag1.s = it->valuestring;
			tag1.len = strlen(tag1.s);
		} else if (strcmp(it->string, "tag2")==0) {
			tag2.s = it->valuestring;
			tag2.len = strlen(tag2.s);
		} else if (strcmp(it->string, "cseq1")==0) {
			cseq1.s = it->valuestring;
			cseq1.len = strlen(cseq1.s);
		} else if (strcmp(it->string, "cseq2")==0) {
			cseq2.s = it->valuestring;
			cseq2.len = strlen(cseq2.s);
		} else if (strcmp(it->string, "route_set1")==0) {
			route_set1.s = it->valuestring;
			route_set1.len = strlen(route_set1.s);
		} else if (strcmp(it->string, "route_set2")==0) {
			route_set2.s = it->valuestring;
			route_set2.len = strlen(route_set2.s);
		} else if (strcmp(it->string, "contact1")==0) {
			contact1.s = it->valuestring;
			contact1.len = strlen(contact1.s);
		} else if (strcmp(it->string, "contact2")==0) {
			contact2.s = it->valuestring;
			contact2.len = strlen(contact2.s);
		} else if (strcmp(it->string, "from_uri")==0) {
			from_uri.s = it->valuestring;
			from_uri.len = strlen(from_uri.s);
		} else if (strcmp(it->string, "to_uri")==0) {
			to_uri.s = it->valuestring;
			to_uri.len = strlen(to_uri.s);
		} else if (strcmp(it->string, "req_uri")==0) {
			req_uri.s = it->valuestring;
			req_uri.len = strlen(req_uri.s);
		} else {
			LM_ERR("unrecognized field in json object\n");
		}
	}

	dlg = dlg_get_by_iuid(&iuid);
	if (dlg) {
		LM_DBG("found dialog [%u:%u] at %p\n", iuid.h_entry, iuid.h_id, dlg);
		unref++;
	}

	switch(action) {
		case DLG_DMQ_UPDATE:
			LM_DBG("Updating dlg [%u:%u] with callid [%.*s]\n", iuid.h_entry, iuid.h_id,
					callid.len, callid.s);
			if (!dlg) {
				dlg = build_new_dlg(&callid, &from_uri, &to_uri, &tag1, &req_uri);
				if (!dlg) {
					LM_ERR("failed to build new dialog\n");
					goto error;
				}

				if(dlg->h_entry != iuid.h_entry){
					LM_ERR("inconsistent hash data from peer: "
						"make sure all Kamailio's use the same hash size\n");
					shm_free(dlg);
					goto error;
				}

				/* link the dialog */
				link_dlg(dlg, 0, 0);
				dlg_set_leg_info(dlg, &tag1, &route_set1, &contact1, &cseq1, 0);
				/* override generated h_id */
				dlg->h_id = iuid.h_id;
				/* prevent DB sync */
				dlg->dflags &= ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED);
				dlg->iflags |= DLG_IFLAG_DMQ_SYNC;
			} else {
				/* remove existing profiles */
				if (dlg->profile_links!=NULL) {
					destroy_linkers(dlg->profile_links);
					dlg->profile_links = NULL;
				}
			}

			dlg->init_ts = init_ts;
			dlg->start_ts = start_ts;

			vj = srjson_GetObjectItem(&jdoc, jdoc.root, "vars");
			if(vj!=NULL) {
				for(it=vj->child; it; it = it->next)
				{
					k.s = it->string;        k.len = strlen(k.s);
					v.s = it->valuestring;   v.len = strlen(v.s);
					set_dlg_variable(dlg, &k, &v);
				}
			}
			/* add profiles */
			if(profiles.s!=NULL) {
				srjson_InitDoc(&prof_jdoc, NULL);
				prof_jdoc.buf = profiles;
				dlg_json_to_profiles(dlg, &prof_jdoc);
				srjson_DestroyDoc(&prof_jdoc);
			}
			if (state == dlg->state) {
				break;
			}
			/* intentional fallthrough */

		case DLG_DMQ_STATE:
			if (!dlg) {
				LM_ERR("dialog [%u:%u] not found\n", iuid.h_entry, iuid.h_id);
				goto error;
			}
			if (state < dlg->state) {
				LM_NOTICE("Ignoring backwards state change on dlg [%u:%u]"
						" with callid [%.*s] from state [%u] to state [%u]\n",
					iuid.h_entry, iuid.h_id,
					dlg->callid.len, dlg->callid.s, dlg->state, state);
				break;
			}
			LM_DBG("State update dlg [%u:%u] with callid [%.*s] from state [%u]"
					" to state [%u]\n", iuid.h_entry, iuid.h_id,
					dlg->callid.len, dlg->callid.s, dlg->state, state);
			switch (state) {
				case DLG_STATE_EARLY:
					dlg->start_ts = start_ts;
					dlg->lifetime = lifetime;
					dlg_set_leg_info(dlg, &tag1, &route_set1, &contact1, &cseq1, 0);
					break;
				case DLG_STATE_CONFIRMED:
					dlg->start_ts = start_ts;
					dlg->lifetime = lifetime;
					dlg_set_leg_info(dlg, &tag1, &route_set1, &contact1, &cseq1, 0);
					dlg_set_leg_info(dlg, &tag2, &route_set2, &contact2, &cseq2, 1);
					if (insert_dlg_timer( &dlg->tl, dlg->lifetime ) != 0) {
						LM_CRIT("Unable to insert dlg timer %p [%u:%u]\n",
							dlg, dlg->h_entry, dlg->h_id);
					} else {
						/* dialog pointer inserted in timer list */
						dlg_ref(dlg, 1);
					}
					break;
				case DLG_STATE_DELETED:
					if (dlg->state == DLG_STATE_CONFIRMED) {
						ret = remove_dialog_timer(&dlg->tl);
						if (ret == 0) {
							/* one extra unref due to removal from timer list */
							unref++;
						} else if (ret < 0) {
							LM_CRIT("unable to unlink the timer on dlg %p [%u:%u]\n",
								dlg, dlg->h_entry, dlg->h_id);
						}
					}
					/* prevent DB sync */
					dlg->dflags |= DLG_FLAG_NEW;
					/* keep dialog around for a bit, to prevent out-of-order
					 * syncs to reestablish the dlg */
					dlg->init_ts = time(NULL);
					break;
				default:
					LM_ERR("unhandled state update to state %u\n", state);
					dlg_unref(dlg, unref);
					goto error;
			}
			dlg->state = state;
			break;

		case DLG_DMQ_RM:
			if (!dlg) {
				LM_DBG("dialog [%u:%u] not found\n", iuid.h_entry, iuid.h_id);
				goto error;
			}
			LM_DBG("Removed dlg [%u:%u] with callid [%.*s] int state [%u]\n",
					iuid.h_entry, iuid.h_id,
					dlg->callid.len, dlg->callid.s, dlg->state);
			if (dlg->state==DLG_STATE_CONFIRMED
					|| dlg->state==DLG_STATE_EARLY) {
				ret = remove_dialog_timer(&dlg->tl);
				if (ret == 0) {
					/* one extra unref due to removal from timer list */
					unref++;
				} else if (ret < 0) {
					LM_CRIT("unable to unlink the timer on dlg %p [%u:%u]\n",
						dlg, dlg->h_entry, dlg->h_id);
				}
			}
			/* prevent DB sync */
			dlg->dflags |= DLG_FLAG_NEW;
			unref++;
			break;

		case DLG_DMQ_SYNC:
			dmq_send_all_dlgs(0);
			break;

		case DLG_DMQ_NONE:
			break;
	}
	if (dlg && unref)
		dlg_unref(dlg, unref);

	srjson_DestroyDoc(&jdoc);
	resp->reason = dmq_200_rpl;
	resp->resp_code = 200;
	return 0;

invalid:
	srjson_DestroyDoc(&jdoc);
invalid2:
	resp->reason = dmq_400_rpl;
	resp->resp_code = 400;
	return 0;

error:
	srjson_DestroyDoc(&jdoc);
	resp->reason = dmq_500_rpl;
	resp->resp_code = 500;
	return 0;
}
Esempio n. 4
0
/*!
 * \brief Create a new dialog from a sip message
 *
 * Create a new dialog from a SIP message, register a callback
 * to keep track of the dialog with help of the tm module.
 * This function is either called from the request callback, or
 * from the dlg_manage function in the configuration script.
 * \see dlg_onreq
 * \see w_dlg_manage
 * \param req SIP message
 * \param t transaction
 * \param run_initial_cbs if set zero, initial callbacks are not executed
 * \return 0 on success, -1 on failure
 */ 
int dlg_new_dialog(sip_msg_t *req, struct cell *t, const int run_initial_cbs)
{
	dlg_cell_t *dlg;
	str s;
	str callid;
    str ftag;
    str ttag;
    str req_uri;
    unsigned int dir;

	dlg = dlg_get_ctx_dialog();
    if(dlg != NULL) {
		dlg_release(dlg);
        return -1;
	}

	if(req->first_line.u.request.method_value != METHOD_INVITE)
		return -1;

    if(pre_match_parse( req, &callid, &ftag, &ttag, 0)<0) {
        LM_WARN("pre-matching failed\n");
        return -1;
    }

    if(ttag.s!=0 && ttag.len!=0)
        return -1;

    if(pv_printf_s(req, ruri_param_model, &req_uri)<0) {
        LM_ERR("error - cannot print the r-uri format\n");
        return -1;
    }
    trim(&req_uri);

	dir = DLG_DIR_NONE;
	/* search dialog by SIP attributes
	 * - hash table slot is left locked  */
	dlg = dlg_search(&callid, &ftag, &ttag, &dir);
	if(dlg) {
		if (detect_spirals) {
			if (spiral_detected == 1) {
				dlg_hash_release(&callid);
				return 0;
			}

			if ( dlg->state != DLG_STATE_DELETED ) {
				LM_DBG("Callid '%.*s' found, must be a spiraled request\n",
					callid.len, callid.s);
				spiral_detected = 1;

				if (run_initial_cbs)
					run_dlg_callbacks( DLGCB_SPIRALED, dlg, req, NULL,
							DLG_DIR_DOWNSTREAM, 0);
				/* set ctx dlg id shortcuts */
				_dlg_ctx.iuid.h_entry = dlg->h_entry;
				_dlg_ctx.iuid.h_id = dlg->h_id;
				/* search_dlg() has incremented the ref count by 1 */
				dlg_release(dlg);
				dlg_hash_release(&callid);
				return 0;
			}
			dlg_release(dlg);
		}
    }
    spiral_detected = 0;

    dlg = build_new_dlg (&callid /*callid*/,
                         &(get_from(req)->uri) /*from uri*/,
                         &(get_to(req)->uri) /*to uri*/,
                         &ftag/*from_tag*/,
                         &req_uri /*r-uri*/ );

	if (dlg==0) {
		dlg_hash_release(&callid);
		LM_ERR("failed to create new dialog\n");
		return -1;
	}

	/* save caller's tag, cseq, contact and record route*/
	if (populate_leg_info(dlg, req, t, DLG_CALLER_LEG,
			&(get_from(req)->tag_value)) !=0) {
		dlg_hash_release(&callid);
		LM_ERR("could not add further info to the dialog\n");
		shm_free(dlg);
		return -1;
	}

	/* Populate initial varlist: */
	dlg->vars = get_local_varlist_pointer(req, 1);

	/* after dlg_search() slot was kept locked */
	link_dlg(dlg, 0, 1);
	/* unlock after dlg_search() */
	dlg_hash_release(&callid);

	dlg->lifetime = get_dlg_timeout(req);
	s.s   = _dlg_ctx.to_route_name;
	s.len = strlen(s.s);
	dlg_set_toroute(dlg, &s);
	dlg->sflags |= _dlg_ctx.flags;
	dlg->iflags |= _dlg_ctx.iflags;

	if (dlg_send_bye!=0 || _dlg_ctx.to_bye!=0)
		dlg->iflags |= DLG_IFLAG_TIMEOUTBYE;

    if (run_initial_cbs)  run_create_callbacks( dlg, req);

	/* first INVITE seen (dialog created, unconfirmed) */
	if ( seq_match_mode!=SEQ_MATCH_NO_ID &&
			add_dlg_rr_param( req, dlg->h_entry, dlg->h_id)<0 ) {
		LM_ERR("failed to add RR param\n");
		goto error;
	}

    if_update_stat( dlg_enable_stats, processed_dlgs, 1);

	_dlg_ctx.cpid = my_pid();
    _dlg_ctx.iuid.h_entry = dlg->h_entry;
    _dlg_ctx.iuid.h_id = dlg->h_id;
    set_current_dialog(req, dlg);

	return 0;

error:
	if (!spiral_detected)
		dlg_unref(dlg, 1);               // undo ref regarding linking
	return -1;
}
Esempio n. 5
0
void dlg_bridge_tm_callback(struct cell *t, int type, struct tmcb_params *ps)
{
	struct sip_msg *msg = NULL;
	dlg_transfer_ctx_t *dtc = NULL;
	struct dlg_cell *dlg = NULL;
	str s;
	str cseq;
	str empty = {"", 0};

	if(ps->param==NULL || *ps->param==0)
	{
		LM_DBG("message id not received\n");
		return;
	}
	dtc = *((dlg_transfer_ctx_t**)ps->param);
	if(dtc==NULL)
		return;
	LM_DBG("completed with status %d\n", ps->code);
	if(ps->code>=300)
		goto error;

	/* 2xx - build dialog/send refer */
	msg = ps->rpl;
	if((msg->cseq==NULL || parse_headers(msg,HDR_CSEQ_F,0)<0)
			|| msg->cseq==NULL || msg->cseq->parsed==NULL)
	{
			LM_ERR("bad sip message or missing CSeq hdr :-/\n");
			goto error;
	}
	cseq = (get_cseq(msg))->number;

	if((msg->to==NULL && parse_headers(msg, HDR_TO_F,0)<0) || msg->to==NULL)
	{
		LM_ERR("bad request or missing TO hdr\n");
		goto error;
	}
	if(parse_from_header(msg))
	{
		LM_ERR("bad request or missing FROM hdr\n");
		goto error;
	}
	if((msg->callid==NULL && parse_headers(msg,HDR_CALLID_F,0)<0)
			|| msg->callid==NULL){
		LM_ERR("bad request or missing CALLID hdr\n");
		goto error;
	}
	s = msg->callid->body;
	trim(&s);

	/* some sanity checks */
	if (s.len==0 || get_from(msg)->tag_value.len==0) {
		LM_ERR("invalid request -> callid (%d) or from TAG (%d) empty\n",
			s.len, get_from(msg)->tag_value.len);
		goto error;
	}

	dlg = build_new_dlg(&s /*callid*/, &(get_from(msg)->uri) /*from uri*/,
		&(get_to(msg)->uri) /*to uri*/,
		&(get_from(msg)->tag_value)/*from_tag*/,
		&(get_to(msg)->uri) /*use to as r-uri*/ );
	if (dlg==0) {
		LM_ERR("failed to create new dialog\n");
		goto error;
	}
	dtc->dlg = dlg;
	if (dlg_set_leg_info(dlg, &(get_from(msg)->tag_value),
				&empty, &dlg_bridge_controller, &cseq, DLG_CALLER_LEG)!=0) {
		LM_ERR("dlg_set_leg_info failed\n");
		goto error;
	}

	if (populate_leg_info(dlg, msg, t, DLG_CALLEE_LEG,
			&(get_to(msg)->tag_value)) !=0)
	{
		LM_ERR("could not add further info to the dialog\n");
		shm_free(dlg);
		goto error;
	}

	if(dlg_refer_callee(dtc)!=0)
		goto error;
	return;

error:
	dlg_transfer_ctx_free(dtc);
	return;
}
Esempio n. 6
0
int dlg_transfer(struct dlg_cell *dlg, str *to, int side)
{
	dlg_transfer_ctx_t *dtc = NULL;
	struct dlg_cell *ndlg = NULL;
	str from;
	str empty = {"", 0};

	dtc = (dlg_transfer_ctx_t*)shm_malloc(sizeof(dlg_transfer_ctx_t));
	if(dtc==NULL)
	{
		LM_ERR("no shm\n");
		return -1;
	}
	if(side==DLG_CALLEE_LEG)
	{
		from = dlg->from_uri;
	} else {
		from = dlg->to_uri;
	}
	memset(dtc, 0, sizeof(dlg_transfer_ctx_t));
	dtc->from.s = (char*)shm_malloc((from.len+1)*sizeof(char));
	if(dtc->from.s==NULL)
	{
		LM_ERR("no shm\n");
		shm_free(dtc);
		return -1;
	}
	dtc->to.s = (char*)shm_malloc((to->len+1)*sizeof(char));
	if(dtc->to.s==NULL)
	{
		LM_ERR("no shm\n");
		shm_free(dtc->from.s);
		shm_free(dtc);
		return -1;
	}
	memcpy(dtc->from.s, from.s, from.len);
	dtc->from.len = from.len;
	dtc->from.s[dtc->from.len] = '\0';
	memcpy(dtc->to.s, to->s, to->len);
	dtc->to.len = to->len;
	dtc->to.s[dtc->to.len] = '\0';
	
	if(side==DLG_CALLER_LEG)
		ndlg = build_new_dlg(&dlg->callid /*callid*/,
				&dlg->to_uri /*from uri*/, &dlg->from_uri /*to uri*/,
				&dlg->tag[side]/*from_tag*/, &dlg->req_uri /*req uri */ );
	else
		ndlg = build_new_dlg(&dlg->callid /*callid*/,
				&dlg->from_uri /*from uri*/, &dlg->to_uri /*to uri*/,
				&dlg->tag[side]/*from_tag*/, &dlg->req_uri /*req uri */ );
	if (ndlg==0) {
		LM_ERR("failed to create new dialog\n");
		goto error;
	}
	dtc->dlg = ndlg;
	if (dlg_set_leg_info(ndlg, &dlg->tag[side], &empty,
			&dlg->contact[side], &dlg->cseq[side], DLG_CALLER_LEG)!=0)
	{
		LM_ERR("dlg_set_leg_info failed for caller\n");
		goto error;
	}
	if(side==DLG_CALLEE_LEG)
		side = DLG_CALLER_LEG;
	else
		side = DLG_CALLEE_LEG;
	if (dlg_set_leg_info(ndlg, &dlg->tag[side], &dlg->route_set[side],
			&dlg->contact[side], &dlg->cseq[side], DLG_CALLEE_LEG)!=0)
	{
		LM_ERR("dlg_set_leg_info failed for caller\n");
		goto error;
	}

	if(dlg_refer_callee(dtc)!=0)
		goto error;
	return 0;

error:
	dlg_transfer_ctx_free(dtc);
	return -1;
}
Esempio n. 7
0
/**
 * replicates the remote update of an ongoing dialog locally
 * by reading the relevant information using the Binary Packet Interface
 */
int dlg_replicated_update(void)
{
	struct dlg_cell *dlg;
	str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles;
	unsigned int dir, dst_leg;
	int timeout, h_entry;
	str st;
	struct dlg_entry *d_entry;

	bin_pop_str(&call_id);
	bin_pop_str(&from_tag);
	bin_pop_str(&to_tag);
	bin_pop_str(&from_uri);
	bin_pop_str(&to_uri);

	LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n",
	call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s,
	from_uri.len, from_uri.s, to_uri.len, to_uri.s);

	dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg);

	h_entry = dlg_hash(&call_id);
	d_entry = &d_table->entries[h_entry];

	dlg_lock(d_table, d_entry);

	if (!dlg) {
		/* TODO: change to LM_DBG */
		LM_ERR("dialog not found, building new\n");

		dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag);
		if (!dlg) {
			LM_ERR("Failed to create replicated dialog!\n");
			goto error;
		}

		return dlg_replicated_create(dlg, &from_tag, &to_tag, 0);
	}

	bin_skip_int(2);
	bin_pop_int(&dlg->state);

	bin_skip_str(2);

	bin_pop_str(&st);
	if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) {
		LM_ERR("failed to update caller cseq\n");
		goto error;
	}

	bin_pop_str(&st);
	if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) {
		LM_ERR("failed to update callee cseq\n");
		goto error;
	}

	bin_skip_str(6);
	bin_pop_str(&vars);
	bin_pop_str(&profiles);
	bin_pop_int(&dlg->user_flags);
	bin_pop_int(&dlg->flags);

	bin_pop_int(&timeout);
	bin_skip_int(2);

	timeout -= time(0);
	LM_DBG("Received updated timeout of %d for dialog %.*s\n",timeout,call_id.len,call_id.s);

	if (dlg->lifetime != timeout) {
		dlg->lifetime = timeout;
		if (update_dlg_timer(&dlg->tl, dlg->lifetime) == -1)
			LM_ERR("failed to update dialog lifetime!\n");
	}

	unref_dlg_unsafe(dlg, 1, d_entry);

	if (vars.s && vars.len != 0)
		read_dialog_vars(vars.s, vars.len, dlg);

	dlg_unlock(d_table, d_entry);

	if (profiles.s && profiles.len != 0)
		read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1);

	return 0;

error:
	dlg_unlock(d_table, d_entry);
	return -1;
}
Esempio n. 8
0
/**
 * replicates a confirmed dialog from another OpenSIPS instance
 * by reading the relevant information using the Binary Packet Interface
 */
int dlg_replicated_create(struct dlg_cell *cell, str *ftag, str *ttag, int safe)
{
	int next_id, h_entry;
	unsigned int dir, dst_leg;
	str callid, from_uri, to_uri, from_tag, to_tag;
	str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu;
	str sock, vars, profiles;
	struct dlg_cell *dlg = NULL;
	struct socket_info *caller_sock, *callee_sock;
	struct dlg_entry *d_entry;

	if_update_stat(dlg_enable_stats, processed_dlgs, 1);

	LM_DBG("Received replicated dialog!\n");
	if (!cell) {
		bin_pop_str(&callid);
		bin_pop_str(&from_tag);
		bin_pop_str(&to_tag);
		bin_pop_str(&from_uri);
		bin_pop_str(&to_uri);

		dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg);

		h_entry = dlg_hash(&callid);
		d_entry = &d_table->entries[h_entry];

		if (safe)
			dlg_lock(d_table, d_entry);

		if (dlg) {
			LM_DBG("Dialog with ci '%.*s' is already created\n",
			       callid.len, callid.s);
			unref_dlg_unsafe(dlg, 1, d_entry);
			dlg_unlock(d_table, d_entry);
			return 0;
		}

		dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag);
		if (!dlg) {
			LM_ERR("Failed to create replicated dialog!\n");
			goto pre_linking_error;
		}
	} else {
		h_entry = dlg_hash(&cell->callid);
		d_entry = &d_table->entries[h_entry];

		if (safe)
			dlg_lock(d_table, d_entry);

		from_tag = *ftag;
		to_tag = *ttag;
		dlg = cell;
	}

	bin_pop_int(&dlg->h_id);
	bin_pop_int(&dlg->start_ts);
	bin_pop_int(&dlg->state);

	next_id = d_table->entries[dlg->h_entry].next_id;

	d_table->entries[dlg->h_entry].next_id =
		(next_id <= dlg->h_id) ? (dlg->h_id + 1) : next_id;

	if (bin_pop_str(&sock))
		goto pre_linking_error;

	caller_sock = fetch_socket_info(&sock);

	if (bin_pop_str(&sock))
		goto pre_linking_error;

	callee_sock = fetch_socket_info(&sock);

	if (!caller_sock || !callee_sock) {
		LM_ERR("Dialog in DB doesn't match any listening sockets\n");
		goto pre_linking_error;
	}

	bin_pop_str(&cseq1);
	bin_pop_str(&cseq2);
	bin_pop_str(&rroute1);
	bin_pop_str(&rroute2);
	bin_pop_str(&contact1);
	bin_pop_str(&contact2);
	bin_pop_str(&mangled_fu);
	bin_pop_str(&mangled_tu);

	/* add the 2 legs */
	if (dlg_add_leg_info(dlg, &from_tag, &rroute1, &contact1,
				&cseq1, caller_sock, 0, 0) != 0 ||
		dlg_add_leg_info(dlg, &to_tag, &rroute2, &contact2,
				&cseq2, callee_sock, &mangled_fu, &mangled_tu) != 0) {
		LM_ERR("dlg_set_leg_info failed\n");
		goto pre_linking_error;
	}

	dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG;

	/* link the dialog into the hash */
	dlg->h_id = d_entry->next_id++;
	if (!d_entry->first)
		d_entry->first = d_entry->last = dlg;
	else {
		d_entry->last->next = dlg;
		dlg->prev = d_entry->last;
		d_entry->last = dlg;
	}
	dlg->ref++;
	d_entry->cnt++;

	bin_pop_str(&vars);
	bin_pop_str(&profiles);
	bin_pop_int(&dlg->user_flags);
	bin_pop_int(&dlg->flags);
	bin_pop_int((void *)&dlg->tl.timeout);
	bin_pop_int(&dlg->legs[DLG_CALLER_LEG].last_gen_cseq);
	bin_pop_int(&dlg->legs[callee_idx(dlg)].last_gen_cseq);

	if (dlg->tl.timeout <= (unsigned int)time(0))
		dlg->tl.timeout = 0;
	else
		dlg->tl.timeout -= (unsigned int)time(0);

	/* restore the timer values */
	if (insert_dlg_timer(&dlg->tl, (int)dlg->tl.timeout) != 0) {
		LM_CRIT("Unable to insert dlg %p [%u:%u] "
			"with clid '%.*s' and tags '%.*s' '%.*s'\n",
			dlg, dlg->h_entry, dlg->h_id,
			dlg->callid.len, dlg->callid.s,
			dlg->legs[DLG_CALLER_LEG].tag.len,
			dlg->legs[DLG_CALLER_LEG].tag.s,
			dlg->legs[callee_idx(dlg)].tag.len,
			ZSW(dlg->legs[callee_idx(dlg)].tag.s));
		goto error;
	}

	if (dlg->state == DLG_STATE_CONFIRMED_NA ||
	    dlg->state == DLG_STATE_CONFIRMED)
		active_dlgs_cnt++;

	/* reference the dialog as kept in the timer list */
	ref_dlg_unsafe(dlg, 1);

	LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n",dlg->tl.timeout,callid.len,callid.s,safe);

	dlg->lifetime = 0;

	/*
	Do not replicate the pinging - we might terminate dialogs badly when running
	as backup 
	if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) {
		if (insert_ping_timer(dlg) != 0)
			LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg);
		else {
			ref_dlg_unsafe(dlg, 1);
		}
	}
	*/

	if (dlg_db_mode == DB_MODE_DELAYED) {
		/* to be later removed by timer */
		ref_dlg_unsafe(dlg, 1);
	}

	if (vars.s && vars.len != 0)
		read_dialog_vars(vars.s, vars.len, dlg);

	dlg_unlock(d_table, d_entry);

	if (profiles.s && profiles.len != 0)
		read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1);

	if_update_stat(dlg_enable_stats, active_dlgs, 1);

	run_load_callback_per_dlg(dlg);

	return 0;

pre_linking_error:
	dlg_unlock(d_table, d_entry);
	if (dlg)
		destroy_dlg(dlg);
	return -1;

error:
	dlg_unlock(d_table, d_entry);
	if (dlg)
		unref_dlg(dlg, 1);

	return -1;
}
Esempio n. 9
0
/**
 * replicates a confirmed dialog from another OpenSIPS instance
 * by reading the relevant information using the Binary Packet Interface
 */
int dlg_replicated_create(bin_packet_t *packet, struct dlg_cell *cell, str *ftag,
							str *ttag, int safe)
{
	int h_entry;
	unsigned int dir, dst_leg;
	str callid = { NULL, 0 }, from_uri, to_uri, from_tag, to_tag;
	str cseq1, cseq2, contact1, contact2, rroute1, rroute2, mangled_fu, mangled_tu;
	str sock, vars, profiles;
	struct dlg_cell *dlg = NULL;
	struct socket_info *caller_sock, *callee_sock;
	struct dlg_entry *d_entry;

	LM_DBG("Received replicated dialog!\n");
	if (!cell) {
		DLG_BIN_POP(str, packet, callid, malformed);
		DLG_BIN_POP(str, packet, from_tag, malformed);
		DLG_BIN_POP(str, packet, to_tag, malformed);
		DLG_BIN_POP(str, packet, from_uri, malformed);
		DLG_BIN_POP(str, packet, to_uri, malformed);

		dlg = get_dlg(&callid, &from_tag, &to_tag, &dir, &dst_leg);

		h_entry = dlg_hash(&callid);
		d_entry = &d_table->entries[h_entry];

		if (safe)
			dlg_lock(d_table, d_entry);

		if (dlg) {
			LM_DBG("Dialog with ci '%.*s' is already created\n",
				callid.len, callid.s);
			unref_dlg_unsafe(dlg, 1, d_entry);
			/* unmark dlg as loaded from DB (otherwise it would have been
			 * dropped later when syncing from cluster is done) */
			dlg->flags &= ~DLG_FLAG_FROM_DB;
			dlg_unlock(d_table, d_entry);
			return 0;
		}

		dlg = build_new_dlg(&callid, &from_uri, &to_uri, &from_tag);
		if (!dlg) {
			LM_ERR("Failed to create replicated dialog!\n");
			goto pre_linking_error;
		}
	} else {
		h_entry = dlg_hash(&cell->callid);
		d_entry = &d_table->entries[h_entry];

		if (safe)
			dlg_lock(d_table, d_entry);

		from_tag = *ftag;
		to_tag = *ttag;
		dlg = cell;
	}
	if_update_stat(dlg_enable_stats, processed_dlgs, 1);

	DLG_BIN_POP(int, packet, dlg->h_id, pre_linking_error);
	DLG_BIN_POP(int, packet, dlg->start_ts, pre_linking_error);
	DLG_BIN_POP(int, packet, dlg->state, pre_linking_error);

	/* next_id follows the max value of all replicated ids */
	if (d_table->entries[dlg->h_entry].next_id <= dlg->h_id)
		d_table->entries[dlg->h_entry].next_id = dlg->h_id + 1;

	DLG_BIN_POP(str, packet, sock, pre_linking_error);

	caller_sock = fetch_socket_info(&sock);

	DLG_BIN_POP(str, packet, sock, pre_linking_error);

	callee_sock = fetch_socket_info(&sock);

	if (!caller_sock || !callee_sock) {
		LM_ERR("Replicated dialog doesn't match any listening sockets\n");
		goto pre_linking_error;
	}

	DLG_BIN_POP(str, packet, cseq1, pre_linking_error);
	DLG_BIN_POP(str, packet, cseq2, pre_linking_error);
	DLG_BIN_POP(str, packet, rroute1, pre_linking_error);
	DLG_BIN_POP(str, packet, rroute2, pre_linking_error);
	DLG_BIN_POP(str, packet, contact1, pre_linking_error);
	DLG_BIN_POP(str, packet, contact2, pre_linking_error);
	DLG_BIN_POP(str, packet, mangled_fu, pre_linking_error);
	DLG_BIN_POP(str, packet, mangled_tu, pre_linking_error);

	/* add the 2 legs */
	/* TODO - sdp here */
	if (dlg_update_leg_info(0, dlg, &from_tag, &rroute1, &contact1,
		&cseq1, caller_sock, 0, 0,0) != 0 ||
		dlg_update_leg_info(1, dlg, &to_tag, &rroute2, &contact2,
		&cseq2, callee_sock, &mangled_fu, &mangled_tu,0) != 0) {
		LM_ERR("dlg_set_leg_info failed\n");
		goto pre_linking_error;
	}

	dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG;

	/* link the dialog into the hash */
	if (!d_entry->first)
		d_entry->first = d_entry->last = dlg;
	else {
		d_entry->last->next = dlg;
		dlg->prev = d_entry->last;
		d_entry->last = dlg;
	}
	dlg->ref++;
	d_entry->cnt++;

	DLG_BIN_POP(str, packet, vars, pre_linking_error);
	DLG_BIN_POP(str, packet, profiles, pre_linking_error);
	DLG_BIN_POP(int, packet, dlg->user_flags, pre_linking_error);
	DLG_BIN_POP(int, packet, dlg->mod_flags, pre_linking_error);

	DLG_BIN_POP(int, packet, dlg->flags, pre_linking_error);
	/* also save the dialog into the DB on this instance */
	dlg->flags |= DLG_FLAG_NEW;

	DLG_BIN_POP(int, packet, dlg->tl.timeout, pre_linking_error);
	DLG_BIN_POP(int, packet, dlg->legs[DLG_CALLER_LEG].last_gen_cseq, pre_linking_error);
	DLG_BIN_POP(int, packet, dlg->legs[callee_idx(dlg)].last_gen_cseq, pre_linking_error);

	if (dlg->tl.timeout <= (unsigned int) time(0))
		dlg->tl.timeout = 0;
	else
		dlg->tl.timeout -= (unsigned int) time(0);

	/* restore the timer values */
	if (insert_dlg_timer(&dlg->tl, (int) dlg->tl.timeout) != 0) {
		LM_CRIT("Unable to insert dlg %p [%u:%u] "
			"with clid '%.*s' and tags '%.*s' '%.*s'\n",
			dlg, dlg->h_entry, dlg->h_id,
			dlg->callid.len, dlg->callid.s,
			dlg->legs[DLG_CALLER_LEG].tag.len,
			dlg->legs[DLG_CALLER_LEG].tag.s,
			dlg->legs[callee_idx(dlg)].tag.len,
			ZSW(dlg->legs[callee_idx(dlg)].tag.s));
		goto error;
	}

	if (dlg->state == DLG_STATE_CONFIRMED_NA ||
		dlg->state == DLG_STATE_CONFIRMED)
		active_dlgs_cnt++;

	/* reference the dialog as kept in the timer list */
	ref_dlg_unsafe(dlg, 1);

	LM_DBG("Received initial timeout of %d for dialog %.*s, safe = %d\n",
		dlg->tl.timeout, callid.len, callid.s, safe);

	dlg->lifetime = 0;

	if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) {
		if (insert_ping_timer(dlg) != 0)
			LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg);
		else {
			ref_dlg_unsafe(dlg, 1);
		}
	}

	if (dlg_db_mode == DB_MODE_DELAYED) {
		/* to be later removed by timer */
		ref_dlg_unsafe(dlg, 1);
	}

	if (vars.s && vars.len != 0)
		read_dialog_vars(vars.s, vars.len, dlg);

	dlg_unlock(d_table, d_entry);

	if (profiles.s && profiles.len != 0)
		read_dialog_profiles(profiles.s, profiles.len, dlg, 0, 1);

	if_update_stat(dlg_enable_stats, active_dlgs, 1);

	run_load_callback_per_dlg(dlg);

	return 0;

pre_linking_error:
	dlg_unlock(d_table, d_entry);
	if (dlg)
		destroy_dlg(dlg);
	return -1;

error:
	dlg_unlock(d_table, d_entry);
	if (dlg)
		unref_dlg(dlg, 1);

malformed:
	return -1;
}
Esempio n. 10
0
/**
 * replicates the remote update of an ongoing dialog locally
 * by reading the relevant information using the Binary Packet Interface
 */
int dlg_replicated_update(bin_packet_t *packet)
{
	struct dlg_cell *dlg;
	str call_id, from_tag, to_tag, from_uri, to_uri, vars, profiles;
	unsigned int dir, dst_leg;
	int timeout, h_entry;
	str st;
	struct dlg_entry *d_entry;
	int rcv_flags, save_new_flag;

	bin_pop_str(packet, &call_id);
	bin_pop_str(packet, &from_tag);
	bin_pop_str(packet, &to_tag);
	bin_pop_str(packet, &from_uri);
	bin_pop_str(packet, &to_uri);

	LM_DBG("replicated update for ['%.*s' '%.*s' '%.*s' '%.*s' '%.*s']\n",
		call_id.len, call_id.s, from_tag.len, from_tag.s, to_tag.len, to_tag.s,
		from_uri.len, from_uri.s, to_uri.len, to_uri.s);

	dlg = get_dlg(&call_id, &from_tag, &to_tag, &dir, &dst_leg);

	h_entry = dlg_hash(&call_id);
	d_entry = &d_table->entries[h_entry];

	dlg_lock(d_table, d_entry);

	if (!dlg) {
		LM_DBG("dialog not found, building new\n");

		dlg = build_new_dlg(&call_id, &from_uri, &to_uri, &from_tag);
		if (!dlg) {
			LM_ERR("Failed to create replicated dialog!\n");
			goto error;
		}

		return dlg_replicated_create(packet ,dlg, &from_tag, &to_tag, 0);
	}

	bin_skip_int(packet, 2);
	bin_pop_int(packet, &dlg->state);

	bin_skip_str(packet, 2);

	bin_pop_str(packet, &st);
	if (dlg_update_cseq(dlg, DLG_CALLER_LEG, &st, 0) != 0) {
		LM_ERR("failed to update caller cseq\n");
		goto error;
	}

	bin_pop_str(packet, &st);
	if (dlg_update_cseq(dlg, callee_idx(dlg), &st, 0) != 0) {
		LM_ERR("failed to update callee cseq\n");
		goto error;
	}

	bin_skip_str(packet, 6);
	bin_pop_str(packet, &vars);
	bin_pop_str(packet, &profiles);
	bin_pop_int(packet, &dlg->user_flags);
	bin_pop_int(packet, &dlg->mod_flags);

	bin_pop_int(packet, &rcv_flags);
	/* make sure an update received immediately after a create can't
	 * incorrectly erase the DLG_FLAG_NEW before locally writing to DB */
	save_new_flag = dlg->flags & DLG_FLAG_NEW;
	dlg->flags = rcv_flags;
	dlg->flags |= ((save_new_flag ? DLG_FLAG_NEW : 0) | DLG_FLAG_CHANGED);

	bin_pop_int(packet, &timeout);
	bin_skip_int(packet, 2);

	timeout -= time(0);
	LM_DBG("Received updated timeout of %d for dialog %.*s\n",
		timeout, call_id.len, call_id.s);

	if (dlg->lifetime != timeout) {
		dlg->lifetime = timeout;
		switch (update_dlg_timer(&dlg->tl, dlg->lifetime) ) {
		case -1:
			LM_ERR("failed to update dialog lifetime!\n");
			/* continue */
		case 0:
			/* timeout value was updated */
			break;
		case 1:
			/* dlg inserted in timer list with new expire (reference it)*/
			ref_dlg(dlg,1);
		}
	}

	unref_dlg_unsafe(dlg, 1, d_entry);

	if (vars.s && vars.len != 0)
		read_dialog_vars(vars.s, vars.len, dlg);

	dlg->flags |= DLG_FLAG_VP_CHANGED;

	dlg_unlock(d_table, d_entry);

	if (profiles.s && profiles.len != 0)
		read_dialog_profiles(profiles.s, profiles.len, dlg, 1, 1);

	return 0;

error:
	dlg_unlock(d_table, d_entry);
	return -1;
}
Esempio n. 11
0
static int load_dialog_info_from_db(int dlg_hash_size)
{
	db_res_t * res;
	db_val_t * values;
	db_row_t * rows;
	int i, nr_rows;
	struct dlg_cell *dlg;
	str callid, from_uri, to_uri, from_tag, to_tag;
	str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu;
	unsigned int next_id;
	int no_rows = 10;
	struct socket_info *caller_sock,*callee_sock;

	res = 0;
	if((nr_rows = select_entire_dialog_table(&res,&no_rows)) < 0)
		goto end;

	nr_rows = RES_ROW_N(res);

	do {
		LM_DBG("loading information from database for %i dialogs\n", nr_rows);

		rows = RES_ROWS(res);

		/* for every row---dialog */
		for(i=0; i<nr_rows; i++){

			values = ROW_VALUES(rows + i);

			if (VAL_NULL(values) || VAL_NULL(values+1)) {
				LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n",
					h_entry_column.len, h_entry_column.s,
					h_id_column.len, h_id_column.s);
				continue;
			}

			if (VAL_NULL(values+7) || VAL_NULL(values+8)) {
				LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n",
					start_time_column.len, start_time_column.s,
					state_column.len, state_column.s);
				continue;
			}

			if ( VAL_INT(values+8) == DLG_STATE_DELETED ) {
				LM_DBG("dialog already terminated -> skipping\n");
				continue;
			}

			caller_sock = create_socket_info(values, 16);
			callee_sock = create_socket_info(values, 17);
			if (caller_sock == NULL || callee_sock == NULL) {
				LM_ERR("Dialog in DB doesn't match any listening sockets");
				continue;
			}

			/*restore the dialog info*/
			GET_STR_VALUE(callid, values, 2, 1, 0);
			GET_STR_VALUE(from_uri, values, 3, 1, 0);
			GET_STR_VALUE(from_tag, values, 4, 1, 0);
			GET_STR_VALUE(to_uri, values, 5, 1, 0);

			if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag))==0){
				LM_ERR("failed to build new dialog\n");
				goto error;
			}

			if(dlg->h_entry != VAL_INT(values)){
				LM_ERR("inconsistent hash data in the dialog database: "
					"you may have restarted opensips using a different "
					"hash_size: please erase %.*s database and restart\n", 
					dialog_table_name.len, dialog_table_name.s);
				shm_free(dlg);
				goto error;
			}

			/*link the dialog*/
			link_dlg(dlg, 0);

			dlg->h_id = VAL_INT(values+1);
			next_id = d_table->entries[dlg->h_entry].next_id;

			d_table->entries[dlg->h_entry].next_id =
				(next_id < dlg->h_id) ? (dlg->h_id+1) : next_id;

			GET_STR_VALUE(to_tag, values, 6, 1, 1);

			dlg->start_ts	= VAL_INT(values+7);

			dlg->state 		= VAL_INT(values+8);
			if (dlg->state==DLG_STATE_CONFIRMED_NA ||
			dlg->state==DLG_STATE_CONFIRMED) {
				active_dlgs_cnt++;
			} else if (dlg->state==DLG_STATE_EARLY) {
				early_dlgs_cnt++;
			}

			GET_STR_VALUE(cseq1, values, 10 , 1, 1);
			GET_STR_VALUE(cseq2, values, 11 , 1, 1);
			GET_STR_VALUE(rroute1, values, 12, 0, 0);
			GET_STR_VALUE(rroute2, values, 13, 0, 0);
			GET_STR_VALUE(contact1, values, 14, 0, 1);
			GET_STR_VALUE(contact2, values, 15, 0, 1);

			GET_STR_VALUE(mangled_fu, values, 24,0,1);
			GET_STR_VALUE(mangled_tu, values, 25,0,1);

			/* add the 2 legs */
			if ( (dlg_add_leg_info( dlg, &from_tag, &rroute1, &contact1,
			&cseq1, caller_sock,0,0)!=0) ||
			(dlg_add_leg_info( dlg, &to_tag, &rroute2, &contact2,
			&cseq2, callee_sock,&mangled_fu,&mangled_tu)!=0) ) {
				LM_ERR("dlg_set_leg_info failed\n");
				/* destroy the dialog */
				unref_dlg(dlg,1);
				continue;
			}
			dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG;

			/* script variables */
			if (!VAL_NULL(values+18))
				read_dialog_vars( VAL_STR(values+18).s,
					VAL_STR(values+18).len, dlg);

			/* profiles */
			if (!VAL_NULL(values+19))
				read_dialog_profiles( VAL_STR(values+19).s,
					strlen(VAL_STR(values+19).s), dlg,0);


			/* script flags */
			if (!VAL_NULL(values+20)) {
				dlg->user_flags = VAL_INT(values+20);
			}

			/* top hiding */
			dlg->flags = VAL_INT(values+23);
			if (dlg_db_mode==DB_MODE_SHUTDOWN)
				dlg->flags |= DLG_FLAG_NEW;

			/* calculcate timeout */
			dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks();
			if (dlg->tl.timeout<=(unsigned int)time(0))
				dlg->tl.timeout = 0;
			else
				dlg->tl.timeout -= (unsigned int)time(0);

			/* restore the timer values */
			if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) {
				LM_CRIT("Unable to insert dlg %p [%u:%u] "
					"with clid '%.*s' and tags '%.*s' '%.*s'\n",
					dlg, dlg->h_entry, dlg->h_id,
					dlg->callid.len, dlg->callid.s,
					dlg->legs[DLG_CALLER_LEG].tag.len,
					dlg->legs[DLG_CALLER_LEG].tag.s,
					dlg->legs[callee_idx(dlg)].tag.len,
					ZSW(dlg->legs[callee_idx(dlg)].tag.s));
				/* destroy the dialog */
				unref_dlg(dlg,1);
				continue;
			}

			/* reference the dialog as kept in the timer list */
			ref_dlg(dlg,1);
			LM_DBG("current dialog timeout is %u\n", dlg->tl.timeout);

			dlg->lifetime = 0;

			dlg->legs[DLG_CALLER_LEG].last_gen_cseq = 
				(unsigned int)(VAL_INT(values+21));
			dlg->legs[callee_idx(dlg)].last_gen_cseq = 
				(unsigned int)(VAL_INT(values+22));

			if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) {
				if (0 != insert_ping_timer(dlg)) 
					LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); 
				else {
					/* reference dialog as kept in ping timer list */
					ref_dlg(dlg,1);
				}
			}

			next_dialog:
			;
		}

		/* any more data to be fetched ?*/
		if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) {
			if (dialog_dbf.fetch_result( dialog_db_handle, &res,no_rows) < 0) {
				LM_ERR("fetching more rows failed\n");
				goto error;
			}
			nr_rows = RES_ROW_N(res);
		} else {
			nr_rows = 0;
		}

	}while (nr_rows>0);

end:
	dialog_dbf.free_result(dialog_db_handle, res);
	return 0;
error:
	dialog_dbf.free_result(dialog_db_handle, res);
	return -1;
}
Esempio n. 12
0
static int sync_dlg_db_mem(void)
{
	db_res_t * res;
	db_val_t * values;
	db_row_t * rows;
	struct dlg_entry *d_entry;
	struct dlg_cell *it,*known_dlg,*dlg=NULL;
	int i, nr_rows,callee_leg_idx,next_id,db_timeout;
	int no_rows = 10;
	unsigned int db_caller_cseq,db_callee_cseq,dlg_caller_cseq,dlg_callee_cseq;
	struct socket_info *caller_sock,*callee_sock;
	str callid, from_uri, to_uri, from_tag, to_tag;
	str cseq1,cseq2,contact1,contact2,rroute1,rroute2,mangled_fu,mangled_tu;

	res = 0;
	if((nr_rows = select_entire_dialog_table(&res,&no_rows)) < 0)
		goto error;

	nr_rows = RES_ROW_N(res);

	do {
		LM_DBG("loading information from database for %i dialogs\n", nr_rows);

		rows = RES_ROWS(res);

		/* for every row---dialog */
		for(i=0; i<nr_rows; i++){

			values = ROW_VALUES(rows + i);

			if (VAL_NULL(values) || VAL_NULL(values+1)) {
				LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n",
					h_entry_column.len, h_entry_column.s,
					h_id_column.len, h_id_column.s);
				continue;
			}

			if (VAL_NULL(values+7) || VAL_NULL(values+8)) {
				LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n",
					start_time_column.len, start_time_column.s,
					state_column.len, state_column.s);
				continue;
			}

			if ( VAL_INT(values+8) == DLG_STATE_DELETED ) {
				LM_DBG("dialog already terminated -> skipping\n");
				continue;
			}

			/*restore the dialog info*/
			GET_STR_VALUE(callid, values, 2, 1, 0);
			GET_STR_VALUE(from_tag, values, 4, 1, 0);
			GET_STR_VALUE(to_tag, values, 6, 1, 1);

			/* TODO - check about hash resize ? maybe hash was lowered & we overflow the hash */
			known_dlg = 0;
			d_entry = &(d_table->entries[VAL_INT(values)]);

			for (it=d_entry->first;it;it=it->next)
				if (it->callid.len == callid.len && 
					it->legs[DLG_CALLER_LEG].tag.len == from_tag.len &&
					memcmp(it->callid.s,callid.s,callid.len)==0 &&
					memcmp(it->legs[DLG_CALLER_LEG].tag.s,from_tag.s,from_tag.len)==0) {
					/* callid & ftag match */
					callee_leg_idx = callee_idx(it);
					if (it->legs[callee_leg_idx].tag.len == to_tag.len &&
						memcmp(it->legs[callee_leg_idx].tag.s,to_tag.s,to_tag.len)==0) {
						/* full dlg match */
						known_dlg = it;
						break;
					}
				}

			if (known_dlg == 0) {
				LM_DBG("First seen dialog - load all stuff - callid = [%.*s]\n",callid.len,callid.s);
				GET_STR_VALUE(from_uri, values, 3, 1, 0);
				GET_STR_VALUE(to_uri, values, 5, 1, 0);

				caller_sock = create_socket_info(values, 16);
				callee_sock = create_socket_info(values, 17);
				if (caller_sock == NULL || callee_sock == NULL) {
					LM_ERR("Dialog in DB doesn't match any listening sockets");
					continue;
				}

				/* first time we see this dialog - build it from scratch */
				if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag))==0){
					LM_ERR("failed to build new dialog\n");
					goto error;
				}

				if(dlg->h_entry != VAL_INT(values)){
					LM_ERR("inconsistent hash data in the dialog database: "
						"you may have restarted opensips using a different "
						"hash_size: please erase %.*s database and restart\n", 
						dialog_table_name.len, dialog_table_name.s);
					shm_free(dlg);
					goto error;
				}

				/*link the dialog*/
				link_dlg(dlg, 0);

				dlg->h_id = VAL_INT(values+1);
				next_id = d_table->entries[dlg->h_entry].next_id;

				d_table->entries[dlg->h_entry].next_id =
					(next_id < dlg->h_id) ? (dlg->h_id+1) : next_id;

				dlg->start_ts	= VAL_INT(values+7);

				dlg->state 		= VAL_INT(values+8);
				if (dlg->state==DLG_STATE_CONFIRMED_NA ||
				dlg->state==DLG_STATE_CONFIRMED) {
					if_update_stat(dlg_enable_stats, active_dlgs, 1);
				} else if (dlg->state==DLG_STATE_EARLY) {
					if_update_stat(dlg_enable_stats, early_dlgs, 1);
				}

				GET_STR_VALUE(cseq1, values, 10 , 1, 1);
				GET_STR_VALUE(cseq2, values, 11 , 1, 1);
				GET_STR_VALUE(rroute1, values, 12, 0, 0);
				GET_STR_VALUE(rroute2, values, 13, 0, 0);
				GET_STR_VALUE(contact1, values, 14, 0, 1);
				GET_STR_VALUE(contact2, values, 15, 0, 1);

				GET_STR_VALUE(mangled_fu, values, 24,0,1);
				GET_STR_VALUE(mangled_tu, values, 25,0,1);

				/* add the 2 legs */
				if ( (dlg_add_leg_info( dlg, &from_tag, &rroute1, &contact1,
				&cseq1, caller_sock,0,0)!=0) ||
				(dlg_add_leg_info( dlg, &to_tag, &rroute2, &contact2,
				&cseq2, callee_sock,&mangled_fu,&mangled_tu)!=0) ) {
					LM_ERR("dlg_set_leg_info failed\n");
					/* destroy the dialog */
					unref_dlg(dlg,1);
					continue;
				}
				dlg->legs_no[DLG_LEG_200OK] = DLG_FIRST_CALLEE_LEG;

				/* script variables */
				if (!VAL_NULL(values+18))
					read_dialog_vars( VAL_STR(values+18).s,
						VAL_STR(values+18).len, dlg);

				/* profiles */
				if (!VAL_NULL(values+19))
					read_dialog_profiles( VAL_STR(values+19).s,
						strlen(VAL_STR(values+19).s), dlg,0);


				/* script flags */
				if (!VAL_NULL(values+20)) {
					dlg->user_flags = VAL_INT(values+20);
				}

				/* top hiding */
				dlg->flags = VAL_INT(values+23);
				if (dlg_db_mode==DB_MODE_SHUTDOWN)
					dlg->flags |= DLG_FLAG_NEW;

				/* calculcate timeout */
				dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + get_ticks();
				if (dlg->tl.timeout<=(unsigned int)time(0))
					dlg->tl.timeout = 0;
				else
					dlg->tl.timeout -= (unsigned int)time(0);

				/* restore the timer values */
				if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) {
					LM_CRIT("Unable to insert dlg %p [%u:%u] "
						"with clid '%.*s' and tags '%.*s' '%.*s'\n",
						dlg, dlg->h_entry, dlg->h_id,
						dlg->callid.len, dlg->callid.s,
						dlg->legs[DLG_CALLER_LEG].tag.len,
						dlg->legs[DLG_CALLER_LEG].tag.s,
						dlg->legs[callee_idx(dlg)].tag.len,
						ZSW(dlg->legs[callee_idx(dlg)].tag.s));
					/* destroy the dialog */
					unref_dlg(dlg,1);
					continue;
				}

				/* reference the dialog as kept in the timer list */
				ref_dlg(dlg,1);
				LM_DBG("current dialog timeout is %u\n", dlg->tl.timeout);

				dlg->lifetime = 0;

				dlg->legs[DLG_CALLER_LEG].last_gen_cseq = 
					(unsigned int)(VAL_INT(values+21));
				dlg->legs[callee_idx(dlg)].last_gen_cseq = 
					(unsigned int)(VAL_INT(values+22));

				if (dlg->flags & DLG_FLAG_PING_CALLER || dlg->flags & DLG_FLAG_PING_CALLEE) {
					if (0 != insert_ping_timer(dlg)) 
						LM_CRIT("Unable to insert dlg %p into ping timer\n",dlg); 
					else {
						/* reference dialog as kept in ping timer list */
						ref_dlg(dlg,1);
					}
				}
			} else {
				/* we already saw this dialog before
				 * check which is the newer version */

				if (known_dlg->state > VAL_INT(values+8)) {
					LM_DBG("mem has a newer state - ignore \n");
					/* we know a newer version compared to the DB
					 * ignore it */
					goto next_dialog;
				} else if (known_dlg->state == VAL_INT(values+8)) {
					LM_DBG("mem has same state as DB \n");
					/* same state :-( no way to tell which is newer */
					
					/* play nice and store longest timeout, although not always correct*/
					db_timeout = (unsigned int)(VAL_INT(values+9)) + 
						get_ticks();
					if (db_timeout<=(unsigned int)time(0))
						db_timeout = 0;
					else
						db_timeout -= (unsigned int)time(0);

					if (known_dlg->tl.timeout < db_timeout)
						known_dlg->tl.timeout = db_timeout;

					/* check with is newer cseq for caller leg */
					if (!VAL_NULL(values+10)) {
						cseq1.s = VAL_STR(values+10).s;
						cseq1.len = strlen(cseq1.s);
						
						str2int(&cseq1,&db_caller_cseq);
						str2int(&known_dlg->legs[DLG_CALLER_LEG].r_cseq,&dlg_caller_cseq);

						/* Is DB cseq newer ? */
						if (db_caller_cseq > dlg_caller_cseq) {
							if (known_dlg->legs[DLG_CALLER_LEG].r_cseq.len < cseq1.len) {
								known_dlg->legs[DLG_CALLER_LEG].r_cseq.s = 
									shm_realloc(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.len);
								if (!known_dlg->legs[DLG_CALLER_LEG].r_cseq.s) {
									LM_ERR("no more shm\n");
									goto next_dialog;
								}
							}
							memcpy(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.s,cseq1.len);
							known_dlg->legs[DLG_CALLER_LEG].r_cseq.len = cseq1.len;
						}
					} else {
						/* DB has a null cseq - just keep 
						 * what we have so far */
						;
					}

					/* check with is newer cseq for caller leg */
					if (!VAL_NULL(values+11)) {
						cseq2.s = VAL_STR(values+11).s;
						cseq2.len = strlen(cseq2.s);

						callee_leg_idx = callee_idx(known_dlg);
						str2int(&cseq2,&db_callee_cseq);
						str2int(&known_dlg->legs[callee_leg_idx].r_cseq,&dlg_callee_cseq);

						/* Is DB cseq newer ? */
						if (db_callee_cseq > dlg_callee_cseq) {
							if (known_dlg->legs[callee_leg_idx].r_cseq.len < cseq2.len) {
								known_dlg->legs[callee_leg_idx].r_cseq.s = 
									shm_realloc(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.len);
								if (!known_dlg->legs[callee_leg_idx].r_cseq.s) {
									LM_ERR("no more shm\n");
									goto next_dialog;
								}
							}
							memcpy(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.s,cseq2.len);
							known_dlg->legs[callee_leg_idx].r_cseq.len = cseq2.len;
						}
					} else {
						/* DB has a null cseq - just keep 
						 * what we have so far */
						;
					}

					/* update ping cseqs, whichever is newer */
					if (known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq <
						(unsigned int)(VAL_INT(values+21)))
						known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq =
							(unsigned int)(VAL_INT(values+21));
					if (known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq <
						(unsigned int)(VAL_INT(values+22)))
						known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq =
							(unsigned int)(VAL_INT(values+22));

					/* update script variables
					 * if already found, delete the old ones
					 * and replace with new one */
					if (!VAL_NULL(values+18))
						read_dialog_vars( VAL_STR(values+18).s,
							VAL_STR(values+18).len, known_dlg);

					/* skip flags - keep what we have - anyway can't tell which is new */

					/* profiles - do not insert into a profile
					 * is dlg is already in that profile*/
					if (!VAL_NULL(values+19))
						read_dialog_profiles( VAL_STR(values+19).s,
							strlen(VAL_STR(values+19).s), known_dlg,1);
				} else {
					/* DB has newer state, just update fields from DB */
					LM_DBG("DB has newer state \n");

					/* set new state */
					known_dlg->state = VAL_INT(values+8);

					/* update timeout */
					known_dlg->tl.timeout = (unsigned int)(VAL_INT(values+9)) + 
						get_ticks();
					if (known_dlg->tl.timeout<=(unsigned int)time(0))
						known_dlg->tl.timeout = 0;
					else
						known_dlg->tl.timeout -= (unsigned int)time(0);

					/* update cseqs */
					if (!VAL_NULL(values+10)) {
						cseq1.s = VAL_STR(values+10).s;
						cseq1.len = strlen(cseq1.s);

						if (known_dlg->legs[DLG_CALLER_LEG].r_cseq.len < cseq1.len) {
							known_dlg->legs[DLG_CALLER_LEG].r_cseq.s = 
								shm_realloc(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.len);
							if (!known_dlg->legs[DLG_CALLER_LEG].r_cseq.s) {
								LM_ERR("no more shm\n");
								goto next_dialog;
							}
						}
						memcpy(known_dlg->legs[DLG_CALLER_LEG].r_cseq.s,cseq1.s,cseq1.len);
						known_dlg->legs[DLG_CALLER_LEG].r_cseq.len = cseq1.len;
					}

					if (!VAL_NULL(values+11)) {
						cseq2.s = VAL_STR(values+11).s;
						cseq2.len = strlen(cseq1.s);
						callee_leg_idx = callee_idx(known_dlg);

						if (known_dlg->legs[callee_leg_idx].r_cseq.len < cseq2.len) {
							known_dlg->legs[callee_leg_idx].r_cseq.s = 
								shm_realloc(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.len);
							if (!known_dlg->legs[callee_leg_idx].r_cseq.s) {
								LM_ERR("no more shm\n");
								goto next_dialog;
							}
						}

						memcpy(known_dlg->legs[callee_leg_idx].r_cseq.s,cseq2.s,cseq2.len);
						known_dlg->legs[callee_leg_idx].r_cseq.len = cseq2.len;
					}

					/* update ping cseqs */
					known_dlg->legs[DLG_CALLER_LEG].last_gen_cseq = 
						(unsigned int)(VAL_INT(values+21));
					known_dlg->legs[callee_idx(known_dlg)].last_gen_cseq = 
						(unsigned int)(VAL_INT(values+22));

					/* update flags */
					known_dlg->flags = VAL_INT(values+23);
					if (dlg_db_mode==DB_MODE_SHUTDOWN)
						known_dlg->flags |= DLG_FLAG_NEW;

					/* update script variables
					 * if already found, delete the old one
					 * and replace with new one */
					if (!VAL_NULL(values+18))
						read_dialog_vars( VAL_STR(values+18).s,
							VAL_STR(values+18).len, known_dlg);

					/* profiles - do not insert into a profile
					 * is dlg is already in that profile*/
					if (!VAL_NULL(values+19))
						read_dialog_profiles( VAL_STR(values+19).s,
							strlen(VAL_STR(values+19).s), known_dlg,1);
				}

			}
			next_dialog:
			;
		}

		/* any more data to be fetched ?*/
		if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH)) {
			if (dialog_dbf.fetch_result( dialog_db_handle, &res,no_rows) < 0) {
				LM_ERR("fetching more rows failed\n");
				goto error;
			}
			nr_rows = RES_ROW_N(res);
		} else {
			nr_rows = 0;
		}

	}while (nr_rows>0);

	return 0;
error:
	return -1;
}
Esempio n. 13
0
int dlg_create_dialog(struct cell* t, struct sip_msg *req,unsigned int flags)
{
	struct dlg_cell *dlg;
	str s;
	int extra_ref;

	/* module is stricly designed for dialog calls */
	if (req->first_line.u.request.method_value!=METHOD_INVITE)
		return -1;

	if ( (!req->to && parse_headers(req, HDR_TO_F,0)<0) || !req->to ) {
		LM_ERR("bad request or missing TO hdr :-/\n");
		return -1;
	}
	s = get_to(req)->tag_value;
	if (s.s!=0 && s.len!=0)
		return -1;

	if ( parse_from_header(req)) {
		LM_ERR("bad request or missing FROM hdr :-/\n");
		return -1;
	}
	if ((!req->callid && parse_headers(req,HDR_CALLID_F,0)<0) || !req->callid){
		LM_ERR("bad request or missing CALLID hdr :-/\n");
		return -1;
	}
	s = req->callid->body;
	trim(&s);

	/* some sanity checks */
	if (s.len==0 || get_from(req)->tag_value.len==0) {
		LM_ERR("invalid request -> callid (%d) or from TAG (%d) empty\n",
			s.len, get_from(req)->tag_value.len);
		return -1;
	}

	dlg = build_new_dlg( &s /*callid*/, &(get_from(req)->uri) /*from uri*/,
		&(get_to(req)->uri) /*to uri*/,
		&(get_from(req)->tag_value)/*from_tag*/ );
	if (dlg==0) {
		LM_ERR("failed to create new dialog\n");
		return -1;
	}

	dlg->flags |= flags;

	/* save caller's tag, cseq, contact and record route*/
	if (init_leg_info(dlg, req, t, &(get_from(req)->tag_value),NULL,NULL ) !=0) {
		LM_ERR("could not add further info to the dialog\n");
		shm_free(dlg);
		return -1;
	}

	/* set current dialog */
	set_current_dialog(dlg);
	last_dst_leg = DLG_FIRST_CALLEE_LEG;

	extra_ref=2; /* extra ref for the callback and current dlg hook */
	if (dlg_db_mode == DB_MODE_DELAYED)
		extra_ref++; /* extra ref for the timer to delete the dialog */
	link_dlg( dlg , extra_ref);

	if ( seq_match_mode!=SEQ_MATCH_NO_ID &&
		add_dlg_rr_param( req, dlg->h_entry, dlg->h_id)<0 ) {
		LM_ERR("failed to add RR param\n");
		goto error;
	}

	if ( d_tmb.register_tmcb( req, t,
				TMCB_RESPONSE_PRE_OUT|TMCB_RESPONSE_FWDED|TMCB_TRANS_CANCELLED,
				dlg_onreply, (void*)dlg, unreference_dialog_create)<0 ) {
		LM_ERR("failed to register TMCB\n");
		goto error;
	}

	/* complete the dialog setup only if transaction aleady exists;
	   if not, wait for the TMCB_REQUEST_IN callback to do this job */
	if (t) {
		/* first INVITE seen (dialog created, unconfirmed) */
		run_create_callbacks( dlg, req);
		LM_DBG("t hash_index = %u, t label = %u\n",t->hash_index,t->label);
		dlg->initial_t_hash_index = t->hash_index;
		dlg->initial_t_label = t->label;
		dlg->lifetime = get_dlg_timeout(req);

		t->dialog_ctx = (void*) dlg;
		dlg->flags |= DLG_FLAG_ISINIT;
	}

	if_update_stat( dlg_enable_stats, processed_dlgs, 1);

	return 0;
error:
	unref_dlg(dlg,2);
	dialog_cleanup( req, NULL);
	if_update_stat(dlg_enable_stats, failed_dlgs, 1);
	return -1;
}