/** * Function that releases a call in early or early200 situation * early200 is when the callee has already sent out 200 but that hasn't * arrived yet to the caller * @param d - p_dialog of the call * @situation - flag to distinguish between two situations * @return 0 on error 1 on success * * \note This function shouldn't be called directly! * use release_call_early or release_call_early200 instead * * \note This function is full of tricks to fake states and * to decieve the transaction module so that it lets us * end a call in weird state * * \note any move in the order of functions to clarify the structure * can lead to a crash in P-CSCF so watch out! */ int release_call_previous(p_dialog *d,enum release_call_situation situation,int reason_code,str reason_text) { struct cell* t; p_dialog *o; enum p_dialog_direction odir; int i; str r; str hdrs={0,0}; char buf[256]; LOG(L_INFO,"DBG:"M_NAME":release_call_previous(): Releasing call <%.*s> DIR[%d].\n", d->call_id.len,d->call_id.s,d->direction); r.len = snprintf(buf,256,"%.*s%d%.*s%.*s%.*s", reason_hdr_s.len,reason_hdr_s.s, reason_code, reason_hdr_1.len,reason_hdr_1.s, reason_text.len,reason_text.s, reason_hdr_e.len,reason_hdr_e.s); r.s = buf; hdrs.len = r.len+content_length_s.len; hdrs.s = pkg_malloc(hdrs.len); if (!hdrs.s){ LOG(L_INFO,"DBG:"M_NAME":release_call_previous(): Error allocating %d bytes.\n",hdrs.len); hdrs.len=0; goto error; } hdrs.len=0; STR_APPEND(hdrs,r); STR_APPEND(hdrs,content_length_s); /* get the dialog in the other direction to see if something going on there and mark as in releasing */ switch (d->direction){ case DLG_MOBILE_ORIGINATING: odir = DLG_MOBILE_TERMINATING; break; case DLG_MOBILE_TERMINATING: odir = DLG_MOBILE_ORIGINATING; break; default: odir = d->direction; } o = get_p_dialog_dir_nolock(d->call_id,odir); if (o && !o->is_releasing) o->is_releasing = 1; d->is_releasing++; if (d->is_releasing>MAX_TIMES_TO_TRY_TO_RELEASE){ LOG(L_ERR,"ERR:"M_NAME":release_call_previous(): had to delete silently dialog %.*s in direction %i\n",d->call_id.len,d->call_id.s,d->direction); del_p_dialog(d); goto error; } alter_dialog_route_set(d->dialog_c,d->direction); d->state=DLG_STATE_TERMINATED_ONE_SIDE; /*this is just a trick to use the same callback function*/ /*trick or treat!*/ d->dialog_c->state=DLG_CONFIRMED; if (situation == RELEASE_CALL_WEIRD){ send_request(method_ACK_s,hdrs,d->dialog_c,0,0); send_request(method_BYE_s,hdrs,d->dialog_c,confirmed_response,d->direction); //d->dialog_c->state=DLG_EARLY; } else {/*(situation == RELEASE_CALL_EARLY)*/ send_request(method_CANCEL_s,hdrs,d->dialog_c,confirmed_response,d->direction); //d->dialog_c->state=DLG_EARLY; } /*i need the cell of the invite!!*/ /*this is very experimental * and very tricky too*/ t=tmb.t_gett(); if (t && t->uas.request) { /*first trick: i really want to get this reply sent even though we are onreply*/ *tmb.route_mode=MODE_ONFAILURE; /*second trick .. i haven't recieve any response from the uac * if i don't do this i get a cancel sent to the S-CSCF .. its not a big deal*/ /*if i cared about sip forking then probably i would not do that and let the * CANCEL go to the S-CSCF (reread specifications needed)*/ for (i=0; i< t->nr_of_outgoings; i++) t->uac[i].last_received=99; /*t->uas.status=100;*/ /*no one cares about this*/ /*now its safe to do this*/ tmb.t_reply(t->uas.request,reason_code,reason_text.s); *tmb.route_mode=MODE_ONREPLY; tmb.t_release(t->uas.request); /*needed because if not i get last message retransmited... * probably there is a more logical way to do this.. but since i really * want this transaction to end .. whats the point?*/ } return 1; error: if (hdrs.s) pkg_free(hdrs.s); return 0; }
/** * This functions sends BYE for a confirmed dialog * @param d - the p_dialog to end * @param reason - the Reason: header to include in the messages * @returns 0 on error 1 on success */ int release_call_confirmed(p_dialog *d, int reason_code, str reason_text) { enum p_dialog_direction odir; p_dialog *o; str r; str hdrs={0,0}; char buf[256]; LOG(L_INFO,"DBG:"M_NAME":release_call_confirmed(): Releasing call <%.*s> DIR[%d].\n", d->call_id.len,d->call_id.s,d->direction); r.len = snprintf(buf,256,"%.*s%d%.*s%.*s%.*s", reason_hdr_s.len,reason_hdr_s.s, reason_code, reason_hdr_1.len,reason_hdr_1.s, reason_text.len,reason_text.s, reason_hdr_e.len,reason_hdr_e.s); r.s = buf; hdrs.len = r.len+content_length_s.len; hdrs.s = pkg_malloc(hdrs.len); if (!hdrs.s){ LOG(L_INFO,"DBG:"M_NAME":release_call_confirmed(): Error allocating %d bytes.\n",hdrs.len); hdrs.len=0; goto error; } hdrs.len=0; STR_APPEND(hdrs,r); STR_APPEND(hdrs,content_length_s); /* get the dialog in the other direction to see if something going on there and mark as in releasing */ switch (d->direction){ case DLG_MOBILE_ORIGINATING: odir = DLG_MOBILE_TERMINATING; break; case DLG_MOBILE_TERMINATING: odir = DLG_MOBILE_ORIGINATING; break; default: odir = d->direction; } o = get_p_dialog_dir_nolock(d->call_id,odir); if (o && !o->is_releasing) o->is_releasing = 1; d->is_releasing++; if (d->is_releasing>MAX_TIMES_TO_TRY_TO_RELEASE){ LOG(L_ERR,"ERR:"M_NAME":release_call_confirmed(): had to delete silently dialog %.*s in direction %i\n",d->call_id.len,d->call_id.s,d->direction); del_p_dialog(d); goto error; } if (d->is_releasing==1) { /*Before generating a request, we have to generate * the route_set in the dlg , because the route set * in the dialog is for the UAC everything which was in the * Record-Routes (including local address)*/ alter_dialog_route_set(d->dialog_c,d->direction); /*first generate the bye for called user*/ /*then generate the bye for the calling user*/ send_request(method_BYE_s,hdrs,d->dialog_c,confirmed_response,d->direction); send_request(method_BYE_s,hdrs,d->dialog_s,confirmed_response,d->direction); /*the dialog is droped by the callback-function when receives the two replies */ } if (hdrs.s) pkg_free(hdrs.s); return 1; error: if (hdrs.s) pkg_free(hdrs.s); return 0; }
/** * Function that releases a call in early or early200 situation * early200 is when the callee has already sent out 200 but that hasn't * arrived yet to the caller * @param d - p_dialog of the call * @situation - flag to distinguish between two situations * @return 0 on error 1 on success * * \note This function shouldn't be called directly! * use release_call_early or release_call_early200 instead * * \note This function is full of tricks to fake states and * to decieve the transaction module so that it lets us * end a call in weird state * * \note any move in the order of functions to clarify the structure * can lead to a crash in P-CSCF so watch out! */ int release_call_previous(p_dialog *d,enum release_call_situation situation,int reason_code,str reason_text) { struct cell* t; p_dialog *o; enum p_dialog_direction odir; int i; str r; str hdrs= {0,0}; str firstcseq; char buf[256]; if(!d) return 0; LOG(L_INFO,"DBG:"M_NAME":release_call_previous(): Releasing call <%.*s> DIR[%d].\n", d->call_id.len,d->call_id.s,d->direction); r.len = snprintf(buf,256,"%.*s%d%.*s%.*s%.*s", reason_hdr_s.len,reason_hdr_s.s, reason_code, reason_hdr_1.len,reason_hdr_1.s, reason_text.len,reason_text.s, reason_hdr_e.len,reason_hdr_e.s); r.s = buf; hdrs.len = r.len+content_length_s.len; hdrs.s = pkg_malloc(hdrs.len); if (!hdrs.s) { LOG(L_INFO,"DBG:"M_NAME":release_call_previous(): Error allocating %d bytes.\n",hdrs.len); hdrs.len=0; goto error; } hdrs.len=0; STR_APPEND(hdrs,r); STR_APPEND(hdrs,content_length_s); /* get the dialog in the other direction to see if something going on there and mark as in releasing */ switch (d->direction) { case DLG_MOBILE_ORIGINATING: odir = DLG_MOBILE_TERMINATING; break; case DLG_MOBILE_TERMINATING: odir = DLG_MOBILE_ORIGINATING; break; default: odir = d->direction; } time_t time_now=time(0); o = get_p_dialog_dir_nolock(d->call_id,odir); if (o && o->is_releasing==0) { o->is_releasing = 1; // Addition from Alberto Diez the 2nd November 2007 // the idea is to put the other one to expire in TIME_TO_EXPIRE // just in case no reply is received if (o->expires>time_now+TIME_TO_EXPIRE) { o->expires=time_now+TIME_TO_EXPIRE; } } d->is_releasing++; /*The first time i decrease the expire time for the dialog expire , so that i guarantee that * its going to be deleted from the table sometime*/ if (d->expires>time_now+TIME_TO_EXPIRE) { d->expires=time_now+TIME_TO_EXPIRE; } if (d->is_releasing>MAX_TIMES_TO_TRY_TO_RELEASE) { LOG(L_ERR,"ERR:"M_NAME":release_call_previous(): had to delete silently dialog %.*s in direction %i\n",d->call_id.len,d->call_id.s,d->direction); del_p_dialog(d); goto error; } if(alter_dialog_route_set(d->dialog_c,d->direction,situation)<0) { LOG(L_ERR,"ERR:"M_NAME":release_call_previous(): had to delete silently dialog %.*s in direction %i\n",d->call_id.len,d->call_id.s,d->direction); del_p_dialog(d); goto error; } d->state=DLG_STATE_TERMINATED_ONE_SIDE; /*this is just a trick to use the same callback function*/ /*i need the cell of the invite!!*/ /*this is very experimental * and very tricky too*/ //t=tmb.t_gett(); i=0; i=snprintf(buf,256,"%i",d->first_cseq); // i just use buf because its there.. if (i==256) { LOG(L_ERR,"release_call_previous: some client used first CSeq way too big\n"); goto error; } firstcseq.s=pkg_malloc(i+1); // the \0 firstcseq.len=i; sprintf(firstcseq.s,"%i",d->first_cseq); LOG(L_INFO,"CALLED t_lookup_callid with %.*s, %.*s\n",d->call_id.len,d->call_id.s,firstcseq.len,firstcseq.s); if (tmb.t_lookup_callid(&t,d->call_id,firstcseq) < 0) { pkg_free(firstcseq.s); LOG(L_ERR,"release_call_previous: t_lookup_callid failed\n"); goto error; } pkg_free(firstcseq.s); if (t && t!=(void*) -1 && t->uas.request) { if (t->method.len!=6 || t->method.s[0]!='I' || t->method.s[1]!='N' || t->method.s[2]!='V') { //well this is the transaction of a subsequent request within the dialog //and the dialog is not confirmed yet, so its a PRACK or an UPDATE //could also be an options, but the important thing is how am i going to get //the transaction of the invite, that is the one i have to cancel LOG(L_ERR,"this is not my transaction so where am i?\n"); } /*first trick: i really want to get this reply sent even though we are onreply*/ #ifdef SER_MOD_INTERFACE route_type = FAILURE_ROUTE; #else *tmb.route_mode=MODE_ONFAILURE; #endif if (situation == RELEASE_CALL_WEIRD) { /*second trick .. i haven't recieve any response from the uac * if i don't do this i get a cancel sent to the S-CSCF .. its not a big deal*/ /*if i cared about sip forking then probably i would not do that and let the * CANCEL go to the S-CSCF (reread specifications needed)*/ //for (i=0; i< t->nr_of_outgoings; i++) // t->uac[i].last_received=99; /*t->uas.status=100;*/ /*no one cares about this*/ /*now its safe to do this*/ /*trick or treat!*/ for (i=0; i< t->nr_of_outgoings; i++) t->uac[i].last_received=99; t->uas.status=100; d->dialog_c->state=DLG_CONFIRMED; send_request(method_ACK_s,hdrs,d->dialog_c,0,0); send_request(method_BYE_s,hdrs,d->dialog_c,confirmed_response,d->direction); d->dialog_c->state=DLG_EARLY; tmb.t_reply(t->uas.request,reason_code,reason_text.s); *tmb.route_mode=MODE_ONREPLY; t->uas.status=488; tmb.t_release(t->uas.request); /*needed because if not i get last message retransmited... * probably there is a more logical way to do this.. but since i really * want this transaction to end .. whats the point?*/ } else {/*(situation == RELEASE_CALL_EARLY)*/ d->dialog_c->loc_seq.value++; //d->dialog_c->state=DLG_CONFIRMED; //send_request(method_CANCEL_s,hdrs,d->dialog_c,confirmed_response,d->direction); //d->dialog_c->state=DLG_EARLY; tmb.t_reply(t->uas.request,reason_code,reason_text.s); /* for (i=0; i< t->nr_of_outgoings; i++) t->uac[i].last_received=180; */ // t_reply has decided to send a CANCEL already so no big deal! //tmb.cancel_uacs(t,0xFFFF,F_CANCEL_B_FAKE_REPLY); //t->uas.status=488; //tmb.t_release(t->uas.request); // I thought of deleting the dialog here.. but // a good SIP client will respond to the CANCEL with a 487 //del_p_dialog(d); } } if (hdrs.s) pkg_free(hdrs.s); return 1; error: if (hdrs.s) pkg_free(hdrs.s); return 0; }