release_call.c

Go to the documentation of this file.
00001 /*
00002  *  
00003  * Copyright (C) 2004-2006 FhG Fokus
00004  *
00005  * This file is part of Open IMS Core - an open source IMS CSCFs & HSS
00006  * implementation
00007  *
00008  * Open IMS Core is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * For a license to use the Open IMS Core software under conditions
00014  * other than those described here, or to purchase support for this
00015  * software, please contact Fraunhofer FOKUS by e-mail at the following
00016  * addresses:
00017  *     info@open-ims.org
00018  *
00019  * Open IMS Core is distributed in the hope that it will be useful,
00020  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022  * GNU General Public License for more details.
00023  * 
00024  * It has to be noted that this Open Source IMS Core System is not 
00025  * intended to become or act as a product in a commercial context! Its 
00026  * sole purpose is to provide an IMS core reference implementation for 
00027  * IMS technology testing and IMS application prototyping for research 
00028  * purposes, typically performed in IMS test-beds.
00029  * 
00030  * Users of the Open Source IMS Core System have to be aware that IMS
00031  * technology may be subject of patents and licence terms, as being 
00032  * specified within the various IMS-related IETF, ITU-T, ETSI, and 3GPP
00033  * standards. Thus all Open IMS Core users have to take notice of this 
00034  * fact and have to agree to check out carefully before installing, 
00035  * using and extending the Open Source IMS Core System, if related 
00036  * patents and licences may become applicable to the intended usage 
00037  * context.  
00038  *
00039  * You should have received a copy of the GNU General Public License
00040  * along with this program; if not, write to the Free Software
00041  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00042  * 
00043  */
00044  
00045 
00056 #include "release_call.h"
00057 
00058 extern struct tm_binds tmb; 
00059 extern dlg_func_t dialogb;  
00060 
00061 
00062 extern str pcscf_record_route_mo_uri;
00063 extern str pcscf_record_route_mt_uri;
00064 
00065 
00066 
00067 /*only two reasons are possible*/
00068 /*503 service unavailable and 488 not acceptable here*/
00069 /*default is 503 because it fits both the timer and QoS related releases*/
00070 static str reason_hdr_s={"Reason: SIP ;cause=",19};
00071 static str reason_hdr_1={" ;text=\"",8};
00072 static str reason_hdr_e={"\"\r\n",3};
00073 
00074 //static int default_code=503;
00075 //static str _503_text_s={"Service Unavailable",28};
00076 static str _488_text_s={"Not Acceptable Here",19};
00077 
00078 static str method_CANCEL_s={"CANCEL",6};
00079 static str method_ACK_s={"ACK",3};
00080 static str method_BYE_s={"BYE",3};
00081 
00082 
00083 void alter_dialog_route_set(dlg_t *,enum p_dialog_direction);
00084 int send_request(str ,str ,dlg_t *,transaction_cb , enum p_dialog_direction);
00085 void confirmed_response(struct cell *,int ,struct tmcb_params *);
00086 
00087 
00088 static str content_length_s={"Content-Length: 0\r\n",19};
00095 int release_call_confirmed(p_dialog *d, int reason_code, str reason_text)
00096 {
00097     enum p_dialog_direction odir;
00098     p_dialog *o;
00099     str r;
00100     str hdrs={0,0}; 
00101     char buf[256];
00102     
00103     LOG(L_INFO,"DBG:"M_NAME":release_call_confirmed(): Releasing call <%.*s> DIR[%d].\n",
00104         d->call_id.len,d->call_id.s,d->direction);
00105     
00106     r.len = snprintf(buf,256,"%.*s%d%.*s%.*s%.*s",
00107         reason_hdr_s.len,reason_hdr_s.s,
00108         reason_code,
00109         reason_hdr_1.len,reason_hdr_1.s,
00110         reason_text.len,reason_text.s,
00111         reason_hdr_e.len,reason_hdr_e.s);
00112     r.s = buf;
00113 
00114     hdrs.len = r.len+content_length_s.len;  
00115     hdrs.s = pkg_malloc(hdrs.len);
00116     if (!hdrs.s){
00117         LOG(L_INFO,"DBG:"M_NAME":release_call_confirmed(): Error allocating %d bytes.\n",hdrs.len);
00118         hdrs.len=0;
00119         goto error;
00120     }
00121     hdrs.len=0; 
00122     STR_APPEND(hdrs,r);
00123     STR_APPEND(hdrs,content_length_s);  
00124         
00125     /* get the dialog in the other direction to see if something going on there and mark as in releasing */
00126     switch (d->direction){
00127         case DLG_MOBILE_ORIGINATING:
00128             odir = DLG_MOBILE_TERMINATING;
00129             break;
00130         case DLG_MOBILE_TERMINATING:
00131             odir = DLG_MOBILE_ORIGINATING;
00132             break;
00133         default:
00134             odir = d->direction;
00135     }   
00136     
00137     o = get_p_dialog_dir_nolock(d->call_id,odir);
00138     if (o && !o->is_releasing) o->is_releasing = 1;
00139         
00140     d->is_releasing++;
00141         
00142     if (d->is_releasing>MAX_TIMES_TO_TRY_TO_RELEASE){
00143         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);
00144         del_p_dialog(d);
00145         goto error;
00146     }
00147     if (d->is_releasing==1) {   
00148         /*Before generating a request, we have to generate
00149          * the route_set in the dlg , because the route set
00150          * in the dialog is for the UAC everything which was in the 
00151          * Record-Routes (including local address)*/
00152         alter_dialog_route_set(d->dialog_c,d->direction);       
00153         
00154         /*first generate the bye for called user*/
00155         /*then generate the bye for the calling user*/
00156         send_request(method_BYE_s,hdrs,d->dialog_c,confirmed_response,d->direction);
00157         send_request(method_BYE_s,hdrs,d->dialog_s,confirmed_response,d->direction);
00158         
00159         /*the dialog is droped by the callback-function when receives the two replies */
00160     }    
00161 
00162     if (hdrs.s) pkg_free(hdrs.s);
00163     return 1;
00164 error:
00165     if (hdrs.s) pkg_free(hdrs.s);
00166     return 0;   
00167 }
00168 
00169 
00175 void confirmed_response(struct cell *t,int type,struct tmcb_params *ps)
00176 {       
00177     p_dialog *d;
00178     unsigned int hash;
00179     str call_id;
00180     
00181     enum p_dialog_direction dir;
00182     
00183     
00184     if (!ps->param) return;
00185     dir = *((enum p_dialog_direction *) *(ps->param));
00186     shm_free(*ps->param);
00187     *ps->param = 0;     
00188         
00189     //call_id = cscf_get_call_id(ps->rpl,0);
00190     call_id = t->callid;
00191     call_id.s+=9;
00192     call_id.len-=11;
00193     
00194     LOG(L_INFO,"DBG:"M_NAME":confirmed_response(): Received a BYE for a call release for <%.*s> DIR[%d].\n",
00195         call_id.len,call_id.s,dir);
00196     
00197     d = get_p_dialog_dir(call_id,dir);
00198     if (!d) {
00199         LOG(L_ERR,"ERR:"M_NAME":confirmed_response(): Received a BYE for a call release but there is no dialog for <%.*s> DIR[%d].\n",
00200             call_id.len,call_id.s,dir);
00201         return;
00202     }
00203     
00204     if (ps->code>=200){
00205         if (d->state==DLG_STATE_TERMINATED_ONE_SIDE){
00206             hash=d->hash;
00207             del_p_dialog(d);
00208             d_unlock(hash);          
00209         } else {
00210             hash=d->hash;
00211             d->state=DLG_STATE_TERMINATED_ONE_SIDE;
00212             d_unlock(hash);
00213         }       
00214     } 
00215 }
00216 
00217 
00218 
00237 int release_call_previous(p_dialog *d,enum release_call_situation situation,int reason_code,str reason_text)
00238 {
00239     struct cell* t;
00240     p_dialog *o;
00241     enum p_dialog_direction odir;
00242     int i;
00243     str r;
00244     str hdrs={0,0}; 
00245     char buf[256];
00246     
00247     LOG(L_INFO,"DBG:"M_NAME":release_call_previous(): Releasing call <%.*s> DIR[%d].\n",
00248         d->call_id.len,d->call_id.s,d->direction);
00249     
00250     r.len = snprintf(buf,256,"%.*s%d%.*s%.*s%.*s",
00251         reason_hdr_s.len,reason_hdr_s.s,
00252         reason_code,
00253         reason_hdr_1.len,reason_hdr_1.s,
00254         reason_text.len,reason_text.s,
00255         reason_hdr_e.len,reason_hdr_e.s);
00256     r.s = buf;
00257 
00258     hdrs.len = r.len+content_length_s.len;  
00259     hdrs.s = pkg_malloc(hdrs.len);
00260     if (!hdrs.s){
00261         LOG(L_INFO,"DBG:"M_NAME":release_call_previous(): Error allocating %d bytes.\n",hdrs.len);
00262         hdrs.len=0;
00263         goto error;
00264     }
00265     hdrs.len=0;
00266     STR_APPEND(hdrs,r);
00267     STR_APPEND(hdrs,content_length_s);  
00268                                                     
00269     /* get the dialog in the other direction to see if something going on there and mark as in releasing */
00270     switch (d->direction){
00271         case DLG_MOBILE_ORIGINATING:
00272             odir = DLG_MOBILE_TERMINATING;
00273             break;
00274         case DLG_MOBILE_TERMINATING:
00275             odir = DLG_MOBILE_ORIGINATING;
00276             break;
00277         default:
00278             odir = d->direction;
00279     }   
00280     
00281     o = get_p_dialog_dir_nolock(d->call_id,odir);
00282     if (o && !o->is_releasing) o->is_releasing = 1;
00283         
00284     d->is_releasing++;
00285         
00286     if (d->is_releasing>MAX_TIMES_TO_TRY_TO_RELEASE){
00287         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);
00288         del_p_dialog(d);
00289         goto error;
00290     }
00291     
00292     alter_dialog_route_set(d->dialog_c,d->direction);
00293     
00294     d->state=DLG_STATE_TERMINATED_ONE_SIDE;
00295     /*this is just a trick to use the same callback function*/  
00296     
00297     /*trick or treat!*/
00298     d->dialog_c->state=DLG_CONFIRMED;
00299     
00300     if (situation == RELEASE_CALL_WEIRD){
00301         send_request(method_ACK_s,hdrs,d->dialog_c,0,0);
00302         send_request(method_BYE_s,hdrs,d->dialog_c,confirmed_response,d->direction);
00303         //d->dialog_c->state=DLG_EARLY;
00304     } else {/*(situation == RELEASE_CALL_EARLY)*/       
00305         send_request(method_CANCEL_s,hdrs,d->dialog_c,confirmed_response,d->direction);
00306         //d->dialog_c->state=DLG_EARLY;
00307     }
00308 
00309     /*i need the cell of the invite!!*/
00310     /*this is very experimental
00311      * and very tricky too*/
00312     t=tmb.t_gett();
00313     
00314     if (t && t!=(void*) -1  && t->uas.request) {
00315         /*first trick: i really want to get this reply sent even though we are onreply*/
00316         *tmb.route_mode=MODE_ONFAILURE;
00317         
00318         /*second trick .. i haven't recieve any response from the uac
00319          * if i don't do this i get a cancel sent to the S-CSCF .. its not a big deal*/
00320          /*if i cared about sip forking then probably i would not do that and let the 
00321           * CANCEL go to the S-CSCF (reread specifications needed)*/
00322         for (i=0; i< t->nr_of_outgoings; i++)
00323             t->uac[i].last_received=99;
00324         /*t->uas.status=100;*/ /*no one cares about this*/
00325         /*now its safe to do this*/
00326         
00327         tmb.t_reply(t->uas.request,reason_code,reason_text.s);
00328         *tmb.route_mode=MODE_ONREPLY;
00329         tmb.t_release(t->uas.request);
00330 
00331         /*needed because if not i get last message retransmited... 
00332          * probably there is a more logical way to do this.. but since i really
00333          * want this transaction to end .. whats the point?*/
00334     }
00335     
00336     return 1;
00337 error:
00338     if (hdrs.s) pkg_free(hdrs.s);
00339     return 0;   
00340 }
00341 
00342 /*Send cancel and 503 or 488 */
00343 int release_call_early(p_dialog *d,int reason_code,str reason_text)
00344 {
00345     /*CANCEL will be badly routed because the response hasn't being processed
00346      * it will be sent to I-CSCF who will relay to S-CSCF and from there to term@P-CSCF*/
00347     return release_call_previous(d,RELEASE_CALL_EARLY,reason_code,reason_text);
00348 }
00349 /*send ACK,BYE and 503 or 488*/
00350 int release_call_early200(p_dialog *d,int reason_code,str reason_text)
00351 {
00352     return release_call_previous(d,RELEASE_CALL_WEIRD,reason_code,reason_text);
00353 }
00354 
00362  /*
00363   * Any dialogs in state over CONFIRMED are going to be treated by
00364   * release_call_confirmed,  its ok , but its a bit confusing because
00365   * some dialogs which were already treated by release_call_early may 
00366   * end in release_call_confirmed if there is a second call to release_call_p
00367   * and they are already in state DLG_STATE_TERMINATED_ONE_SIDE.. it doesn't really
00368   * matter because both do the same at that point!
00369   */
00370 int release_call_p(p_dialog *d,int reason_code,str reason_text)
00371 {       
00372     if (d->state>=DLG_STATE_CONFIRMED)
00373             return(release_call_confirmed(d,reason_code,reason_text));
00374      else  
00375             return(release_call_early(d,reason_code,reason_text)); 
00376 }
00377 
00378 
00386 int release_call(str callid,int reason_code,str reason_text)
00387 {
00388     p_dialog *d=0;
00389     unsigned int hash;
00390     int res = 0;
00391     
00392     d = get_p_dialog_dir(callid,DLG_MOBILE_ORIGINATING);
00393     if (d) {                
00394         hash = d->hash;
00395         if (release_call_p(d,reason_code,reason_text)>0) res = 1;       
00396         goto done;      
00397     }
00398     d = get_p_dialog_dir(callid,DLG_MOBILE_TERMINATING);
00399     if (d) {                
00400         hash = d->hash;
00401         if (release_call_p(d,reason_code,reason_text)>0) res = 1;       
00402         goto done;
00403     } 
00404     
00405     /*Neither ORGINATING nor TERMINATING is UNKNOWN!*/
00406     /*or doesn't exist*/
00407     /*drop it silently?*/
00408     /*treat it as ORIGINATING or TERMINATING?*/             
00409 done:       
00410     if (d) d_unlock(hash);
00411     return 0;   
00412 }
00413 
00414 
00415 
00426 int P_release_call_onreply(struct sip_msg *msg,char *str1,char *str2)
00427 {
00428     enum p_dialog_direction dir;
00429     p_dialog *d=NULL;
00430     str callid;
00431     struct hdr_field *h1;
00432     str reason={NULL,0};
00433     
00434     if (str2) {
00435         reason.s=str2;
00436         reason.len=strlen(str2);
00437     } else
00438         reason = _488_text_s;
00439         
00440     
00441     dir= (str1[0]=='o' || str1[0]=='O' || str1[0]=='0')? DLG_MOBILE_ORIGINATING : DLG_MOBILE_TERMINATING;
00442     
00443     if (msg->first_line.type== SIP_REQUEST)
00444     {
00445         LOG(L_ERR,"ERR: P_release_call_on_reply called with a request\n");
00446         return CSCF_RETURN_FALSE;
00447     }
00448     
00449     callid=cscf_get_call_id(msg,&h1);
00450     if (is_p_dialog_dir(callid,dir)) {
00451         d=get_p_dialog_dir(callid,dir);
00452         if (msg->first_line.u.reply.statuscode > 199)
00453         {
00454             release_call_previous(d,RELEASE_CALL_WEIRD,488,reason);
00455             d_unlock(d->hash);
00456             return CSCF_RETURN_TRUE;
00457         } else {
00458             release_call_previous(d,RELEASE_CALL_EARLY,488,reason);
00459             d_unlock(d->hash);
00460             return CSCF_RETURN_TRUE;
00461         }
00462     } else {
00463         LOG(L_ERR,"ERR:"M_NAME "P_release_call_onreply :  unable to find dialog\n");
00464         return CSCF_RETURN_BREAK;
00465     }
00466     
00467 }
00477 int send_request(str method,str reqbuf,dlg_t *d,transaction_cb cb, enum p_dialog_direction dir)
00478 {
00479     if((d!=NULL) && (method.s!=NULL))
00480     {
00481         enum p_dialog_direction *cbp=NULL;
00482         
00483         if (method.len !=3 || memcmp(method.s,"ACK",3)!=0)
00484         {
00485             /*In case of ACK i don't want to even send the direction
00486              * moreover as the memory is freed in the callback function
00487              * and the ACK is never replied it would be a bug if i did*/
00488             cbp = shm_malloc(sizeof(enum p_dialog_direction));
00489             if (!cbp){
00490                 LOG(L_ERR,"ERR:"M_NAME":send_request(): error allocating %d bytes\n",sizeof(enum p_dialog_direction));
00491                 return 0;
00492             }
00493             *cbp=dir;
00494         }   
00495         dialogb.request_inside(&method,&reqbuf,NULL, d,cb,cbp);
00496         return 1;
00497     }
00498     
00499     return 0;
00500 }
00501 
00502 
00503 
00511 void alter_dialog_route_set(dlg_t *d,enum p_dialog_direction dir)
00512 {
00513     rr_t *r,*r_new; 
00514     str p; /*this is going to point to the scscf uri*/
00515         
00516     
00517     
00518     switch (dir) {
00519         case DLG_MOBILE_ORIGINATING:
00520             p = pcscf_record_route_mo_uri;
00521             break;
00522         case DLG_MOBILE_TERMINATING:
00523             p = pcscf_record_route_mt_uri;
00524             break;
00525         default:
00526             return;
00527     }
00528     //LOG(L_CRIT,"Looking for <%.*s> in\n",p.len,p.s);
00529     //for(r=d->route_set;r!=NULL;r=r->next) 
00530     //  LOG(L_CRIT,"<%.*s>\n",r->nameaddr.uri.len,r->nameaddr.uri.s);
00531         
00532     for(r=d->route_set;r!=NULL;r=r->next) {
00533         if (r->nameaddr.uri.len>=p.len && 
00534             strncasecmp(r->nameaddr.uri.s,p.s,r->nameaddr.uri.len)==0)
00535             {
00536                 r_new=r->next;
00537                 r->next=NULL;
00538                 shm_free_rr(&d->route_set);
00539                 d->route_set = r_new;
00540                 return; 
00541             }
00542                     
00543     }   
00544 }       
00545 
00546 

Generated on Thu Oct 23 04:14:39 2008 for Open IMS Core CSCFs by  doxygen 1.5.2